pytest-dev / pytest-dev/pytest

class-scoped fixtures VS test subclasses

Open
#13,882 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

topic: fixtures type: question
Dominant language
Python
Stars
14.5k
Forks
3.4k
Avg merge
2d 9h
Merged PRs (30d)
35

Description

Hi,

I'm trying to build a base class to hold a common test setup and assumptions, that I want to enable in subclasses (inspired by this article) having the fixture-powered setup be dependent on class variables. However this approach clashes with class-scoped fixtures which are not run in subclasses of test classes, so any effect or class variables initialized there are not available. I don't know if it's a feature or a bug.

I wonder if this is a bug, if the fixtures should be called in __init_subclass__() for example. Or maybe if an additional "subclass" scope would be of interest or just legit to have?

Here a concrete example, modifying the code from the docs https://docs.pytest.org/en/stable/how-to/fixtures.html#running-multiple-assert-statements-safely

I'm also open to any other suggestion to get a similar result. Passing fixtures as subclasses variable was also not successful.


# contents of tests/end_to_end/test_login.py
from unittest.mock import MagicMock
from urllib.parse import urljoin
from uuid import uuid4

import pytest

Chrome = MagicMock()
AdminApiClient = MagicMock()
User = MagicMock()
LandingPage = MagicMock()
LoginPage = MagicMock()


@pytest.fixture(scope="class")
def admin_client(base_url, admin_credentials):
    return AdminApiClient(base_url, **admin_credentials)


@pytest.fixture(scope="class")
def user(admin_client):
    _user = User(name="Susan", username=f"testuser-{uuid4()}", password="P4$$word")
    admin_client.create_user(_user)
    yield _user
    admin_client.delete_user(_user)


@pytest.fixture(scope="class")
def driver():
    _driver = Chrome()
    yield _driver
    _driver.quit()


@pytest.fixture(scope="class")
def landing_page(driver, login):
    return LandingPage(driver)


class TestLandingPageSuccessBase:

    __test__ = False  # Hide from test discovery

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        cls.__test__ = True  # Enable test discovery for subclasses

    api_version: str  # To be defined in subclasses

    # Unfortunately, login is not called in subclasses!!!
    @pytest.fixture(scope="class", autouse=True)
    def login(self, driver, base_url, user):
        driver.get(urljoin(base_url, f"/{self.api_version}/login"))
        page = LoginPage(driver)
        page.login(user)

    def test_name_in_header(self, landing_page, user):
        assert landing_page.header == f"Welcome, {user.name}!"

    def test_sign_out_button(self, landing_page):
        assert landing_page.sign_out_button.is_displayed()

    def test_profile_link(self, base_url, landing_page, user):
        profile_href = urljoin(base_url, f"/profile?id={user.profile_id}")
        assert landing_page.profile_link.get_attribute("href") == profile_href


class TestLandingPageSuccessV1(TestLandingPageSuccessBase):

    api_version: str = 'v1'


class TestLandingPageSuccessV2(TestLandingPageSuccessBase):

    api_version: str = 'v2'

Running locally on Python 3.14.0 and pytest 8.4.2.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the example in tests/end_to_end/test_login.py and trace how the class-scoped, autouse login fixture is collected for TestLandingPageSuccessV1 and TestLandingPageSuccessV2. Read the interaction between init_subclass(), test discovery, and inherited fixtures. Done means the behavior is explained and either corrected with regression coverage or clearly documented as intended.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.