Home > Blockchain >  Python Unittest: How to initialize selenium in a class and avoid having the browser opening twice?
Python Unittest: How to initialize selenium in a class and avoid having the browser opening twice?

Time:01-20

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)

  • Related