Home > Software design >  Using a class-based test as a fixture
Using a class-based test as a fixture

Time:12-05

I am using this class which creates my login test:

import pytest
from pages.loginPage import LoginPage
from utils import utilis as utils


@pytest.mark.usefixtures("test_setup")
class TestLogin():


    def test_login(self):
        driver=self.driver
        driver.get(utils.URL)

        login =LoginPage(driver)
        login.enterUsername(utils.USERNAME)
        login.enterPassword(utils.PASSWORD)
        login.clickLogin()

I want to re-use this test as a fixture for other tests, like this:

import pytest

from pages.loginPage import LoginPage
from pages.homePage import HomePage
from utils import utilis as util


@pytest.mark.usefixtures("test_login")
class TestAddRegulation():

    def test_addRegulation(self):
        driver = self.driver
        homepage = HomePage(driver)
        homepage.clickRegulationTile()
        homepage.clickAddRegulationListItem()

And this is the conftest.py file with the test_setup fixture:

from selenium import webdriver
import pytest

def pytest_addoption(parser):
    parser.addoption("--browser", action="store",
                     default="chrome",
                     help="Type in browser name e.g.chrome OR firefox")

@pytest.fixture(scope="class")
def test_setup(request):
    browser = request.config.getoption("--browser")
    if browser == 'chrome':
        driver = webdriver.Chrome(executable_path=
                                  r"C:/Users/user/PycharmProjects/RCM_AutomationFramework/drivers/chromedriver.exe")
    elif browser == 'firefox':
        driver = webdriver.Firefox(executable_path=
                                  r"C:/Users/user/PycharmProjects/RCM_AutomationFramework/drivers/geckodriver.exe")
    driver.implicitly_wait(5)
    driver.maximize_window()
    request.cls.driver = driver
    yield
    driver.close()
    driver.quit()
    print("Test is finished")

I can't get this to work, even if the test_login case is executed before the test_addRegulation test case.

I tried marking test_login as a fixture but it doesn't work. I can make it work if I dropped using classes.

Can I make a class method a fixture that is re-usable for other test classes?

CodePudding user response:

Fixtures can be methods defined in a class, but then they are not available outside of the class. As the pytest documentation on fixtures states:

Fixture availability is determined from the perspective of the test. A fixture is only available for tests to request if they are in the scope that fixture is defined in. If a fixture is defined inside a class, it can only be requested by tests inside that class.

(Bold emphasis mine).

This means that you have to use a plain function to define re-usable fixtures. You can still access the class used by each test, however, via the request.cls attribute. Make sure to have the fixture take both the request and the test_setup scopes:

@pytest.fixture(scope="class")
def login(request, test_setup):
    driver = request.cls.driver
    driver.get(utils.URL)

    login = LoginPage(driver)
    login.enterUsername(utils.USERNAME)
    login.enterPassword(utils.PASSWORD)
    login.clickLogin()

Just put that fixture in your conftest.py file. You can use a different scope, provided it doesn't exceed the class scope of the test_setup fixture (so your choices are class and function here).

You can then use that fixture with no actual test body to test the login:

@pytest.mark.usefixtures("login")
class TestLogin:

    def test_login(self):
        # test passes if the login fixture completes.
        pass

This does seem a bit redundant, of course.

Use the fixture for other classes the same way:

@pytest.mark.usefixtures("login")
class TestAddRegulation:

    def test_addRegulation(self):
        # ... etc.

A quick demo (without selenium, just plain Python):

import pytest

@pytest.fixture(scope="class")
def test_setup(request):
    request.cls.fixtures = ["test_setup"]
    yield
    print("Test is finished, fixtures used:", request.cls.fixtures)

@pytest.fixture(scope="class")
def login(request, test_setup):
    # The fixtures list was created by the test_setup fixture
    fixtures = request.cls.fixtures
    fixtures.append("login")

@pytest.mark.usefixtures("login")
class TestLogin:

    def test_login(self):
        assert self.fixtures == ["test_setup", "login"]

@pytest.mark.usefixtures("login")
class TestAddRegulation:

    def test_addRegulation(self):
        assert self.fixtures == ["test_setup", "login"]

Running these tests with pytest -vs (verbose mode, disabling stdout capture) produces:

...::TestLogin::test_login PASSEDTest is finished, fixtures used: ['test_setup', 'login']

...::TestAddRegulation::test_addRegulation PASSEDTest is finished, fixtures used: ['test_setup', 'login']
  • Related