Repository navigation
Suggestion: add new 'parametrization' fixture scope #1552
Description
Currently, a fixture can be scoped as function, class, module, or session.
I propose a fixture can also be scoped per parametrization.
Consider:
import time
@pytest.fixture
def slow_setup():
time.sleep(1)
return 'foo'
If I have a group of tests which I am parametrizing, but that would share this value, it'll do the setup for each one (as expected):
# takes 4 seconds
@pytest.mark.parametrize('bar', [1, 2, 3, 4])
def test_parametrized(slow_setup, bar):
assert slow_setup == 'foo'
If I wanted to run the same group of tests but sharing the fixture, I only really have one option (considering that other tests in the same module need the fixture to be re-set up each time, so I can't use module or session scope): set the fixture scope to class, and put the tests inside a class:
@pytest.fixture(scope='class')
def slow_setup_cls():
time.sleep(1)
return 'foo'
# takes 1 second
class TestParametrized(object):
@pytest.mark.parametrize('bar', [1, 2, 3, 4])
def test_parametrized(self, slow_setup_cls, bar):
assert slow_setup_cls == 'foo'
However, the meaning I really intend to convey is "this fixture should be shared across all parametrizations of a function". I propose the following:
@pytest.fixture(scope='parametrization')
def slow_setup_pmt():
time.sleep(1)
return 'foo'
# takes 1 second
@pytest.mark.parametrize('bar', [1, 2, 3, 4])
def test_parametrized(self, slow_setup_pmt, bar):
assert slow_setup_pmt == 'foo'
This fixture scope conveys, to me: "All parametrizations of this test share the same setup and teardown with regards to this fixture."
Activity
RonnyPfannschmidt commentedon May 7, 2016
i like the idea
currently the proposal has 2 semantical problems
SalmonMode commentedon Aug 24, 2018
Why not just have parametrized fixtures that have
autouseasTrue(factoring in dependencies, of course) make multiple versions of the scopes they are designated for?For example, currently, given the following module:
You get the following flow:
But since
autouseisTrue, the whole of that scope should be impacted. That, to me, would mean that I want to go through that scope, in it's entirety, once for each param set, as each set should impact the whole scope on its own. Here's the flow I would expect in this case:In this case,
param_fixmade two versions of theTestItclass, one for each param set.If
autouseis notTrue, then just only make multiple versions of the individual tests that depend on it.This covers @csaftoiu's example with
pytest.mark.parametrizewithout adding a need to use another argument or alternate scope options.SalmonMode commentedon Aug 28, 2018
So after doing some digging into pytest, it looks like my initial idea of having multiple versions of each scope would pretty much require a total rework of test collection/execution. But I did find what I think is a good place to make a small change so that autouse parametrized fixtures cause other fixtures for that scope to re-execute as well.
I'm writing up a pull request now to put in this change. If it's decided to not go this route, and instead add a new argument/scope to trigger this kind of behavior, I think the changes I've made so far can be easily modified to do that instead.
toby-w commentedon Dec 14, 2018
Hi @RonnyPfannschmidt, I am not clear on the two semantical issues that adding a parametrization scope would raise. Could you provide an example of how this would be a problem?
RonnyPfannschmidt commentedon Dec 14, 2018
conftest.py:test_a.py:what should be the order of tests and the setup/teardown in that case
Sup3rGeo commentedon Mar 16, 2019
If I understand it correctly, it means that in terms of levels, it should be
session > module > class > parameterization > function?Then the example would not be valid @RonnyPfannschmidt , as
test_paramhas amodulescope, depending on aparametrizationscope.--
Actually the
functionnow is in fact every single test case, and thisparametrizationscope would be in fact one per function definition.In fact I think it would be more accurately defined as:
SalmonMode commentedon Mar 16, 2019
@toby-w @Sup3rGeo I think what @RonnyPfannschmidt is referring to, is the fact that you can apply a parametrization within multiple scopes (i.e. session/package/module/class/function), and having a "param" scope doesn't specify which level of parametrization you would want to target.
For example:
The question is what level of parametrization are you targeting with
some_param_lvl_fix? Parametrization is happening at several points, and at multiple levels, but a scope ofparamis not specific enough to say where you want to actually have this fixture reside in the order of operations. You're right that the intention with what @csaftoiu wanted was just to address that weird scope for functions where parametrization doesn't behave the same way as larger scopes do (i.e. parametrization happening after a fixture of the same scope doesn't trigger re-execution of the prior fixture), but "param"/"parametrization" just isn't specific enough of a term.I also don't think @csaftoiu's proposal would actually solve the problem, because a scope of "param"/"parametrization" would suggest that it happens for each parameter set, meaning it would still run the slow running fixture more than once. Granted, the "param" (or whatever it would be called) scope of that type could be used to go back to the current behavior, while the standard "function" level could be made to operate like the larger scopes. But that would likely break a lot of previously written tests, so it's a lose-lose situation.
Sup3rGeo commentedon Mar 16, 2019
@SalmonMode yep, so what I wrote was my understanding of the feature @csaftoiu described in this issue, which I am convinced is different from what @RonnyPfannschmidt understood.
As I understand, the OP wants to have this:
With only two fixture invocations:
test_parametrized_one(and all its [1] [2] [3] [4] instances);test_parametrized_two(and all its [5] [6] [7] [8] instances);Currently, if the
slow_setupscope isfunction, you are going to have 8 invocations. If it ismodule, then you are going to have only 1 invocation.Thats why he uses the workaround of having classes:
Then we get the desired two invocation scenario.
maxrothman commentedon Apr 1, 2020
Wenzel commentedon Oct 24, 2021
RonnyPfannschmidt commentedon Aug 1, 2026
Superseded by the
"definition"fixture scope in #14805 (part of #3926).@pytest.fixture(scope="definition")shares one instance across all parameter sets of a single test definition and nothing beyond it — the semantics requested here.My 2016 objection that "there is no collection element for the function that is not parameterized" is resolved by the new
ItemDefinitionnode, opted into viacollect_function_definition.The second objection — parametrized fixtures at class/module/session scope multiplying their whole enclosing scope — is a separate feature and not covered here; please open a fresh issue if that's the use case you're after.
RonnyPfannschmidt commentedon Aug 1, 2026
closing as the definition scope will solve most of the perceived problem