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
{{ message }}
Repository navigation
Allow to dynamically create fixture in plugin #12376
I am the author of next-actions/pytest-mh which is a multihost testing plugin for pytest. We are doing some black magic to dynamically create test fixtures and map them to some custom objects. Some user-focused details are described here but TLDR we are doing this:
In this scenario, client and ldap are passed to the test_example by setting it in item.funcargs in runtest hook to internal objects created per-tests. pytest_fixture is standard pytest fixture. This works nicely and it is using only public fields (I'm not sure if funcargs is documented, but at the very least it is not prefix with _).
However, accessing these data from pytest fixture fails, obviously:
At this moment, you can only create fixture using @pytest.fixture, decorated functions are collected and registered as fixtures at some point of pytest setup, right?
In pytest-mh, we are passing custom values to each test. The values are objects that are unique for each tests and they are dynamically created as needed by the test. For example:
This will pass to the test client and ldap objects that provide high-level API to manage relevant services. We can't convert these parameters into fixtures using @pytest.fixture decorator and static definition since they really need to be created dynamically for each test for various reasons.
One of the reason is a thing we call "topology parametrization", which means that single test can be run against different hosts (backends) and then parameter called provider can take different values based on the topology that is currently being executed). It's something like @pytest.mark.parametrize but on different level of abstraction.
The way I am doing it currently is by putting these parameters into item.funcargs, which works nicely. The downside is, that they can not be accessed from fixtures. So if I try this:
Then pytest raises exception because client is not a fixture and it is not available. When digging through the code, I found this commit: 3234c79 which is pretty much what I need, only still private. The comment shows the intention to make this public at some point therefore I decided to open this request.
What I would like to have is an option to dynamically register and unregister fixture. So when I am at pytest_runtest_setup hook, I'd like to register a fixture client so it is available to both the test and the function-scoped fixture and I want to unregister the fixture when the code gets into pytest_runtest_teardown.
I hope this clarifies things. Please, let me know if it is still unclear.
@pbrezina Yes it is my intention to make register_function public in some way. Also see discussion in #11662. There are still some design issues we need to work on, like deciding how to expose it (FixtureManager is private so it needs to be exposed in some other way) and dulling a few sharp edges (clear error if not run during collection; difference between None and '' nodeid).
However, I don't think it it will satisfy your requirements, since in pytest fixtures must be registered during the collection phase, and there is no way to unregister them, while you want to do it on setup/teardown.
@nicoddemus The implementation itself does need to get at the FixtureManager. So it needs to take config: Config, or alternatively fetch it from a Node.
Ah missed the type. The minor problem here is one I hinted at above, namely that there a case where node=None (fixturedef.has_location == False), which is subtly different from registering on node=Session (has_location = True):
But I don't really like that it is using internal pytest knowledge (__pytest_wrapped__.obj.func), though I suppose this stuff can be considered stable?
And of course, it quite a hack and it requires user to use type hints. But this works:
This is something I ran into today, I'm writing some code that creates a session-scoped fixture from a function-scoped fixture, in order to be used with some preloader code, to help optimize some heavily used fixtures.
Currently I'm working around this by attaching the generated fixture to the returned fixture, and it can then be exposed as normal, e.g.
preloaded_fixture then adds preload_my_fixture to a list which gets used in our django_db_setup fixture.
This setup lets us avoid having to separately manage a test database that we use via templating, which I know is the usual recommended approach for this.
It'd be great to be able to skip that requirement for us to do preload_my_fixture = my_fixture.preloader.
Activity
RonnyPfannschmidt commentedon May 28, 2024
The ask here is very unclear
Unfortunately it's pretty much a open research question
pbrezina commentedon May 28, 2024
What part should I clarify?
RonnyPfannschmidt commentedon May 28, 2024
Both the ask here and the documentation of the plugin are entirely unclear
I'm not going to guess and I'm not going to try and figure it from the source code
pbrezina commentedon May 28, 2024
At this moment, you can only create fixture using
@pytest.fixture, decorated functions are collected and registered as fixtures at some point of pytest setup, right?In pytest-mh, we are passing custom values to each test. The values are objects that are unique for each tests and they are dynamically created as needed by the test. For example:
This will pass to the test client and ldap objects that provide high-level API to manage relevant services. We can't convert these parameters into fixtures using
@pytest.fixturedecorator and static definition since they really need to be created dynamically for each test for various reasons.One of the reason is a thing we call "topology parametrization", which means that single test can be run against different hosts (backends) and then parameter called
providercan take different values based on the topology that is currently being executed). It's something like@pytest.mark.parametrizebut on different level of abstraction.The way I am doing it currently is by putting these parameters into
item.funcargs, which works nicely. The downside is, that they can not be accessed from fixtures. So if I try this:Then pytest raises exception because
clientis not a fixture and it is not available. When digging through the code, I found this commit: 3234c79 which is pretty much what I need, only still private. The comment shows the intention to make this public at some point therefore I decided to open this request.What I would like to have is an option to dynamically register and unregister fixture. So when I am at
pytest_runtest_setuphook, I'd like to register a fixtureclientso it is available to both the test and the function-scoped fixture and I want to unregister the fixture when the code gets intopytest_runtest_teardown.I hope this clarifies things. Please, let me know if it is still unclear.
bluetech commentedon May 28, 2024
@pbrezina Yes it is my intention to make
register_functionpublic in some way. Also see discussion in #11662. There are still some design issues we need to work on, like deciding how to expose it (FixtureManager is private so it needs to be exposed in some other way) and dulling a few sharp edges (clear error if not run during collection; difference betweenNoneand''nodeid).However, I don't think it it will satisfy your requirements, since in pytest fixtures must be registered during the collection phase, and there is no way to unregister them, while you want to do it on setup/teardown.
nicoddemus commentedon May 28, 2024
Just to comment on this bit:
I don't think we need to expose FixtureManager, perhaps just a public function would suffice?
Say:
bluetech commentedon May 28, 2024
@nicoddemus The implementation itself does need to get at the FixtureManager. So it needs to take
config: Config, or alternatively fetch it from aNode.nicoddemus commentedon May 28, 2024
Yep, that's why I added
Nodeto the signature (besides needing to anchor the fixture at some location for visibility purposes of course).bluetech commentedon May 28, 2024
Ah missed the type. The minor problem here is one I hinted at above, namely that there a case where
node=None(fixturedef.has_location == False), which is subtly different from registering onnode=Session(has_location = True):pytest/src/_pytest/fixtures.py
Lines 1668 to 1676 in 48cb8a2
nicoddemus commentedon May 28, 2024
I see, well no problem adding a
configparameter too and makelocationOptional.pbrezina commentedon May 29, 2024
Well... having this functionality would be very nice.
In the mean time, I have implemented the following workaround:
But I don't really like that it is using internal pytest knowledge (
__pytest_wrapped__.obj.func), though I suppose this stuff can be considered stable?And of course, it quite a hack and it requires user to use type hints. But this works:
robertfw commentedon Sep 21, 2025
This is something I ran into today, I'm writing some code that creates a session-scoped fixture from a function-scoped fixture, in order to be used with some preloader code, to help optimize some heavily used fixtures.
Currently I'm working around this by attaching the generated fixture to the returned fixture, and it can then be exposed as normal, e.g.
preloaded_fixturethen addspreload_my_fixtureto a list which gets used in ourdjango_db_setupfixture.This setup lets us avoid having to separately manage a test database that we use via templating, which I know is the usual recommended approach for this.
It'd be great to be able to skip that requirement for us to do
preload_my_fixture = my_fixture.preloader.Add `pytest.register_fixture`
chore(deps): update dependency pytest to <9.1.1,>=9.1.0 (#340)
chore(deps): update dependency pytest to <9.1.1,>=9.1.0 (#454)
chore(deps): update dependency pytest to <9.1.1,>=9.1.0 (#225)
chore(deps): update dependency pytest to <9.1.1,>=9.1.0 (#263)
chore(deps): update dependency pytest to <9.1.1,>=9.1.0 (#590)
chore(deps): update dependency pytest to <9.1.1,>=9.1.0 (#456)
⬆️ Update dependency pytest to v9.1.1 (#24)
[SECURITY] Update dependency pytest to v9 [SECURITY] (#2)
chore(deps): update root dev dependencies to v9.1.0
Update dependency pytest to v9 (#35)