Consider the example below, since I'm initializing the driver in setUp
method and using it in test_login
, the browser will open twice, the first time during setUp
and then it will be closed and the tests will begin.
If I remove the logic from setUp
and put it in test_login
, the driver will be undefined in test_profile
and tearDown
What's the correct way to initialize the driver and use it throughout the class while not causing the browser to open twice?
from selenium import webdriver
import unittest
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
class Test(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install()))
self.driver.get('https://example.com/login')
self.current_url = self.driver.current_url
self.dashboard_url = 'https://example.com/dashboard'
def test_login(self):
self.assertEqual(self.dashboard_url, self.current_url)
def test_profile(self):
self.driver.get('https://example.com/profile')
def tearDown(self):
self.driver.close()
CodePudding user response:
You need to use setUpClass / tearDownClass:
import unittest
class Test(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
print('setUpClass')
@classmethod
def tearDownClass(cls) -> None:
print('tearDownClass')
def setUp(self):
print('setUp')
def test_login(self):
print('login')
def test_profile(self):
print('profile')
def tearDown(self):
print('tearDown')
CodePudding user response:
Here's an example that uses unittest.TestCase
with Selenium. It has the setUp()
and tearDown()
steps, and it gets the desired behavior you're looking for, although probably with more than you asked for.
You can run it with python -m unittest
:
import sys
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from unittest import TestCase
class RefinedRawSelenium(TestCase):
def setUp(self):
self.driver = None
options = webdriver.ChromeOptions()
options.add_argument("--disable-notifications")
if "linux" in sys.platform:
options.add_argument("--headless=chrome")
options.add_experimental_option(
"excludeSwitches", ["enable-automation", "enable-logging"],
)
prefs = {
"credentials_enable_service": False,
"profile.password_manager_enabled": False,
}
options.add_experimental_option("prefs", prefs)
self.driver = webdriver.Chrome(options=options)
def tearDown(self):
if self.driver:
try:
if self.driver.service.process:
self.driver.quit()
except Exception:
pass
def wait_for_element_visible(
self, selector, by="css selector", timeout=10
):
try:
return WebDriverWait(self.driver, timeout).until(
EC.visibility_of_element_located((by, selector))
)
except Exception:
raise Exception(
"Element {%s} was not visible after %s seconds!"
% (selector, timeout)
)
def wait_for_element_clickable(
self, selector, by="css selector", timeout=10
):
try:
return WebDriverWait(self.driver, timeout).until(
EC.element_to_be_clickable((by, selector))
)
except Exception:
raise Exception(
"Element {%s} was not visible/clickable after %s seconds!"
% (selector, timeout)
)
def wait_for_element_not_visible(
self, selector, by="css selector", timeout=10
):
try:
return WebDriverWait(self.driver, timeout).until(
EC.invisibility_of_element((by, selector))
)
except Exception:
raise Exception(
"Element {%s} was still visible after %s seconds!"
% (selector, timeout)
)
def open(self, url):
self.driver.get(url)
def click(self, selector, by="css selector", timeout=7):
el = self.wait_for_element_clickable(selector, by=by, timeout=timeout)
el.click()
def type(self, selector, text, by="css selector", timeout=10):
el = self.wait_for_element_clickable(selector, by=by, timeout=timeout)
el.clear()
if not text.endswith("\n"):
el.send_keys(text)
else:
el.send_keys(text[:-1])
el.submit()
def assert_element(self, selector, by="css selector", timeout=7):
self.wait_for_element_visible(selector, by=by, timeout=timeout)
def assert_text(self, text, selector="html", by="css selector", timeout=7):
el = self.wait_for_element_visible(selector, by=by, timeout=timeout)
self.assertIn(text, el.text)
def assert_exact_text(self, text, selector, by="css selector", timeout=7):
el = self.wait_for_element_visible(selector, by=by, timeout=timeout)
self.assertEqual(text, el.text)
def assert_element_not_visible(
self, selector, by="css selector", timeout=7
):
self.wait_for_element_not_visible(selector, by=by, timeout=timeout)
def test_add_item_to_cart(self):
self.open("https://www.saucedemo.com")
self.type("#user-name", "standard_user")
self.type("#password", "secret_sauce\n")
self.assert_element("div.inventory_list")
self.assert_text("PRODUCTS", "span.title")
self.click('button[name*="backpack"]')
self.click("#shopping_cart_container a")
self.assert_exact_text("YOUR CART", "span.title")
self.assert_text("Backpack", "div.cart_item")
self.click("#remove-sauce-labs-backpack")
self.assert_element_not_visible("div.cart_item")
self.click("#react-burger-menu-btn")
self.click("a#logout_sidebar_link")
self.assert_element("input#login-button")
# When run with "python" instead of "pytest" or "python -m unittest"
if __name__ == "__main__":
from unittest import main
main()
That was taken from my example in SeleniumBase/examples/migration/raw_selenium/refined_raw.py
It'll actually be part of my session at SeleniumConf 2023 this year (https://seleniumconf.com/agenda/#python-selenium-fundamentals-to-frameworks-with-seleniumbase), where I'll be demoing how to simplify that to something like this, which uses unittest.TestCase
behind the scenes in an import:
from seleniumbase import BaseCase
class CleanSeleniumBase(BaseCase):
def test_add_item_to_cart(self):
self.open("https://www.saucedemo.com")
self.type("#user-name", "standard_user")
self.type("#password", "secret_sauce\n")
self.assert_element("div.inventory_list")
self.assert_text("PRODUCTS", "span.title")
self.click('button[name*="backpack"]')
self.click("#shopping_cart_container a")
self.assert_exact_text("YOUR CART", "span.title")
self.assert_text("Backpack", "div.cart_item")
self.click("#remove-sauce-labs-backpack")
self.assert_element_not_visible("div.cart_item")
self.click("#react-burger-menu-btn")
self.click("a#logout_sidebar_link")
self.assert_element("input#login-button")
# When run with "python" instead of "pytest"
if __name__ == "__main__":
from pytest import main
main([__file__])
(That one uses seleniumbase.BaseCase
, which inherits unittest.TestCase
. Example from SeleniumBase/examples/migration/raw_selenium/simple_sbase.py)