pytest-dev / pytest-dev/pytest

run class scoped parametrized fixture's dependency fixture's teardown after each test class iteration

Open
#9,427 1 comment 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

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

Description

Problem:

The conftest.py fixture's setup/teardown is not executed after each test class iteration.

When running pytest test_foo.py --setup-plan, the SETUP C conf_fixt is only executed before the first parametrized class iteration, and TEARDOWN C conf_fixt is only executed after the last parameterized class iteration is complete.

Related Files:

conftest.py

import pytest
from random import randint

@pytest.fixture(scope='class')
def conf_fixt():
    y = randint(1, 100000)
    yield y
    print(f'teardown: {y}')

test_foo.py

from buildspecs.foo.conftest import conf_fixt
import pytest

@pytest.mark.parametrize("scenario", [
    ("scenario_1"),
    ("scenario_2")
], scope='class')

class TestZoo:
    @pytest.fixture(scope="class")
    def scenario_1(self):
        yield 'scenario_1'

    @pytest.fixture(scope="class")
    def scenario_2(self):
        yield 'scenario_2'

    def test_scenario(self, scenario, request, conf_fixt):
        print(f'scenario value: {request.getfixturevalue(scenario)}')
        print(f'conf_fixt value: {conf_fixt}')

    def test_other(self, scenario, request, conf_fixt):
        print(f'scenario value: {request.getfixturevalue(scenario)}')
        print(f'conf_fixt value: {conf_fixt}')

Actual:

pytest test_foo.py

platform linux -- Python 3.9.8, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /src
plugins: mock-3.6.1
collected 4 items                                                                                   

test_foo.py scenario value: scenario_1
conf_fixt value: 111111
.scenario value: scenario_1
conf_fixt value: 111111
.scenario value: scenario_2
conf_fixt value: 111111
.scenario value: scenario_2
conf_fixt value: 111111
.teardown: 111111

pytest test_foo.py --setup-plan

platform linux -- Python 3.9.8, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /src
plugins: mock-3.6.1
collected 2 items                                                                                   

test_foo.py 
      SETUP    C conf_fixt
      SETUP    C scenario['scenario_1']
        buildspecs/foo/test_foo.py::TestZoo::test_scenario[scenario_1] (fixtures used: conf_fixt, request, scenario)
      TEARDOWN C scenario['scenario_1']
      SETUP    C scenario['scenario_2']
        buildspecs/foo/test_foo.py::TestZoo::test_scenario[scenario_2] (fixtures used: conf_fixt, request, scenario)
      TEARDOWN C scenario['scenario_2']
      TEARDOWN C conf_fixt

Expected:

pytest test_foo.py

platform linux -- Python 3.9.8, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /src
plugins: mock-3.6.1
collected 4 items                                                                                   

test_foo.py scenario value: scenario_1
conf_fixt value: 111111
.scenario value: scenario_1
conf_fixt value: 111111
.teardown: 111111
.scenario value: scenario_2
conf_fixt value: 222222
.scenario value: scenario_2
conf_fixt value: 222222
.teardown: 222222

pytest test_foo.py --setup-plan

platform linux -- Python 3.9.8, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /src
plugins: mock-3.6.1
collected 2 items                                                                                   

test_foo.py 
      SETUP    C conf_fixt
      SETUP    C scenario['scenario_1']
        buildspecs/foo/test_foo.py::TestZoo::test_scenario[scenario_1] (fixtures used: conf_fixt, request, scenario)
      TEARDOWN C scenario['scenario_1']
      ###Expected teardown
      TEARDOWN C conf_fixt 
      ###
      SETUP    C scenario['scenario_2']
        buildspecs/foo/test_foo.py::TestZoo::test_scenario[scenario_2] (fixtures used: conf_fixt, request, scenario)
      TEARDOWN C scenario['scenario_2']
      TEARDOWN C conf_fixt

Attempts:

Add the conf_fixt fixture to the pytest.marks.parametrize() decorator like so:

import pytest

@pytest.mark.parametrize("scenario,conf_fixt_param", [
    ("scenario_1", "conf_fixt"),
    ("scenario_2", "conf_fixt")
], scope='class')

class TestZoo:
    @pytest.fixture(scope="class")
    def scenario_1(self):
        yield 'scenario_1'

    @pytest.fixture(scope="class")
    def scenario_2(self):
        yield 'scenario_2'

    def test_scenario(self, scenario, request, conf_fixt_param):
        print(f'scenario value: {request.getfixturevalue(scenario)}')
        print(f'conf_fixt value: {request.getfixturevalue(conf_fixt_param)}')


    def test_other(self, scenario, request, conf_fixt_param):
        print(f'scenario value: {request.getfixturevalue(scenario)}')
        print(f'conf_fixt value: {request.getfixturevalue(conf_fixt_param)}')

Same Actual and Expected output as the original problem.

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 class-scoped parametrization with the provided conftest.py and test_foo.py examples, including --setup-plan. Trace pytest's fixture setup and teardown ordering for each class parameter, then add a regression test covering the expected per-iteration teardown and verify that both the normal output and setup plan match the expected behavior.

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.