You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hello, I've got a question about class parametrisation. I'm looking for a pytest native way to parametrize a test class with a class-scoped fixture, which doesn't make fixture exist multiple times at once with each of the parameters.
Let's say I've got some test where class-based fixture is used to make the test faster, something like this. The fixture some_fixture generates and inserts some object into the database, and it takes a while, let's say ten seconds. The whole class therefore takes 10 seconds to run, as the some_fixture fixture gets reused for the two tests.
classTestSomething:
@pytest.fixture(scope="class", autouse=True)defsome_fixture(self):
time.sleep(10) # let's say this takes ten seconds, e.g. creating some object in the databasepassdeftest_1(self, some_fixture):
passdeftest_2(self, some_fixture):
pass
Now, let's say the code I'm testing has changed and there's two variants and I want to test both using the same test, just with some parameter. The obvious to do this is to use pytest.mark.parametrize("variant", ["a", "b"]) on the class. The trouble now starts. By default, this adds a variant fixture to the class with function scope. This means, that I need to remove class scope from some_fixture, which makes the test twice as slow. But at least it will work fine in most cases.
@pytest.mark.parametrize("variant", ["a", "b"])classTestSomething:
@pytest.fixture(autouse=True)defsome_fixture(self, variant):
time.sleep(10) # generate the object using the variant fixturepassdeftest_1(self, some_fixture):
passdeftest_2(self, some_fixture):
pass
There's an option to add a scope="class" parameter to the pytest.mark.parametrize, which makes variant a class-scope fixture. This does however break the tests if the tests assume that there's only one object in the database created by some_fixture.
Desired behaviour
What I would like to do is to run the whole TestSomething with each of the variants, but not such that the some_fixture fixture being available in the scope at the same time with each of the variants, basically make it safe for the test to still assume that there's only one object in the database.
Attempted solutions
Base Class
One solution is to define a base class for the tests, and then subclass for each of the variants, something like this:
classBaseSomethingTest(abc.ABC):
@property@abc.abstractmethoddefvariant(self):
pass@pytest.fixture(scope="class", autouse=True)defsome_fixture(self):
time.sleep(10) # generate the object using self.variantpassdeftest_1(self, some_fixture):
passdeftest_2(self, some_fixture):
passclassTestSomethingA(BaseSomethingTest):
variant="a"classTestSomethingB(BaseSomethingTest):
variant="b"
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
Hello, I've got a question about class parametrisation. I'm looking for a pytest native way to parametrize a test class with a class-scoped fixture, which doesn't make fixture exist multiple times at once with each of the parameters.
The code examples are also stored in this repo with all imports: https://github.com/mikicz/symmetrical-engine
Setup
Let's say I've got some test where class-based fixture is used to make the test faster, something like this. The fixture
some_fixture
generates and inserts some object into the database, and it takes a while, let's say ten seconds. The whole class therefore takes 10 seconds to run, as thesome_fixture
fixture gets reused for the two tests.Test output
Now, let's say the code I'm testing has changed and there's two variants and I want to test both using the same test, just with some parameter. The obvious to do this is to use
pytest.mark.parametrize("variant", ["a", "b"])
on the class. The trouble now starts. By default, this adds avariant
fixture to the class with function scope. This means, that I need to remove class scope fromsome_fixture
, which makes the test twice as slow. But at least it will work fine in most cases.Test output
There's an option to add a
scope="class"
parameter to thepytest.mark.parametrize
, which makesvariant
a class-scope fixture. This does however break the tests if the tests assume that there's only one object in the database created bysome_fixture
.Desired behaviour
What I would like to do is to run the whole
TestSomething
with each of the variants, but not such that thesome_fixture
fixture being available in the scope at the same time with each of the variants, basically make it safe for the test to still assume that there's only one object in the database.Attempted solutions
Base Class
One solution is to define a base class for the tests, and then subclass for each of the variants, something like this:
Test output
This does work, but there's downsides:
variant
as a fixture, but to use the propertyCursed Metaprogramming
I did develop a way to basically automate the subclass approach, but it's quite ugly metaprogramming:
Test output
So this solves the two issues I have with the base class approach, but it's not the prettiest solution.
Conclusion
So the question I have is, is there a native pytest way to do what I did in the attempted solutions?
Beta Was this translation helpful? Give feedback.
All reactions