New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Idea: @pytest.mark.parametrize on @pytest.fixture #3960
Comments
…ark.parametrize` on a fixture. Therefore one can use multiple `@cases_data` decorators, too. Fixes #19. Note: this is a temporary feature, that will be removed if/when pytest supports it, see pytest-dev/pytest#3960.
I implemented a workaround from pytest_cases import pytest_fixture_plus
STEREO_PATHS = ['stereo 1.wav', 'stereo 2.wav']
@pytest_fixture_plus(scope="module")
@pytest.mark.parametrize("path", STEREO_PATHS)
@pytest.mark.parametrize("cfg_factory", [list, dict])
def stereo_cfg(path, cfg_factory):
... It has been tested against pytest 2.x and 3.x. It will remain available until this issue is directly fixed in It is also compliant with Note: it works by creating sub-fixtures under the hood, just like the ones you showed in your workaround. So thanks for the idea! :) |
Update: the execution order was a bit random, I fixed it in 1.2.0, see smarie/python-pytest-cases#22 . |
I would love to see this implemented, as I see no reason why we cannot use the same way to parameterize fixtures and test functions. @smarie does your improved fixture accept also parameterizing pair of arguments? like:
|
yes it does support multi-parameters. Let me know (please post an issue) if it does not work for you. @pytest_fixture_plus
@pytest.mark.parametrize("arg1, arg2", [
("a", "b"),
("c", "d"),
]
def my_fixture(arg1, arg2):
pass |
I created an issue in: smarie/python-pytest-cases#26 |
This directly ties into a critical topic - how should marks pass from fixture to test, there is no simple solution |
@Sup3rGeo this was a regression, the issue is now fixed in @RonnyPfannschmidt my two cents on this topic for what's worth: if the problem is passing marks, then it might be worth considering to make the parametrization decorator a first-class citizen and not a mark anymore. And/or to provide a parametrization decorator that "feels the same" for users, but is implemented differently whether you parametrize test functions or fixtures. Implementation can then rely on marks if needed, but that's an implementation decision, not user's business. |
@smarie another issue name-spacing the new parameters - parameters are fixtures with direct or indirect values, so they naturally spill to the test in some way |
If I understand your comment correctly, you are saying that what is proposed in pytest-cases and above is not an ideal longterm solution. I fully agree, it is indeed a workaround. I try to make the generated fixture names explicit and unique (see source):
But they are still generated fixtures, so that's not entirely transparent. At least it allows us to work while this makes its way in the core |
Following your comment and smarie/python-pytest-cases#28 I changed the design in The only problem is that I have to generate the ids for the user (when they are not explicitly provided) and I would like to do it exactly like pytest. When no explicit |
idmaker and its related functions in |
thanks ! |
Hi @RonnyPfannschmidt , just to let you know that after making this feature, @Sup3rGeo also made what I think is a great suggestion: providing users with an API to create "parameter" fixtures, that is, parametrized fixtures whose value is the parameter value. This could naturally be extended to the fact that all test parameters created by This enables very neat test architectures where you can safely reuse parameters within test functions and fixtures without having to refactor your whole code or do complex In my opinion it would be a great evolution for pytest, definitely simplifying a lot of issues I personally encountered during test refactoring. See an example implementation here if you wish to play with it: https://smarie.github.io/python-pytest-cases/#param_fixtures |
@smarie im currently not on active duty and i suspect it will be quite a long time before i take a look at something like this (as quite frankly getting the details right on that kind of system is just a horrendous mess of details) |
No worries, I understand that you have put a lot of efforts to bring In the meantime that's absolutely not a problem, since |
Any updates on this ? Mixing the @pytest.fixture
@pytest.mark.parametrize('v', [0, 1])
def my_fixture(v):
return v |
@Conchylicultor, just mentioning something that works very nicely, but might not be what you need: @pytest.fixture(name="v")
def my_fixture(request):
# do some stuff with request.param here
yield request.param
# then do some stuff with request.param post-tests
@pytest.mark.parametrize(
"v", [0, 1], indirect=True,
)
def test_my(v):
print(f"{v} from test") ...might be of help in such cases, it has its own set of limitations, though. |
@Conchylicultor there is a workaround until this is baked in from pytest_cases import fixture
@fixture
@pytest.mark.parametrize('v', [0, 1])
def my_fixture(v):
return v |
@smarie Yes, I'm aware of @tanev, this feature request is about parametrize the fixture, not the test. You solution only works mostly when a single test is parametrized. Also the indirection makes it too "magic". |
In near future its impossible to do in core What looks simply has a massive amount of edge cases we currently can't sanely manage, so for now, marks won't work for fixtures |
Looks like making |
Just to add to @RonnyPfannschmidt's response: the main issue about adding this support is not parametrization per-se, but adding mark support for fixtures, which needs some careful consideration of the repercussions of that. The "core" implementation of parametrization in pytest is This is how the pytest core implements parametrization support out of the box for general use:
|
It seems to me that it might be that the The parametrization of fixtures from @smarie seems to be already what me and other uses typically need and expect from such feature - at the same time, I don't see how it is or should be related to the problem related to passing marks from fixtures to tests. Generally speaking, I see a completely different usage for general |
You are correct in that it could be treated as separate concerns, I just wanted to address explicitly the title of the issue ("why isn't @pytest.mark.parametrize work on fixures already?"). To elaborate:
A new I'm pretty sure the idea above can be implemented outside the core even, if someone wants to play with it. Having said that, I'm not a big fan of the idea in principle: creating another way to do the same thing is confusing for users and a maintenance problem for the project (both in code to maintain and the possibility of bugs that will arise from that implementation). Conceptually, I think supporting marks in fixtures makes sense (and in turn make fixture parametrization that much nicer), but we need to think carefully what it means. For parametrize, the answer is "obvious": @pytest.mark.parametrize("x", [1, 2])
def fix(x):
return x * 10
def test(fix):
assert fix in (100, 200) But not so much in the general sense: @pytest.mark.X(hello="world")
def fix():
return 1
def test(fix):
assert fix == 1 What happens with "X"? Are marks applied to fixtures are automatically propagated to tests which use it? How about the mark arguments, Internally, there's another problem, marks are applied to Nodes (test functions and classes), and fixtures are not part of the collection tree. All those details need to figure out first. |
Thanks for the detailed answer @nicoddemus
Fair point! If I understood correctly from your words:
Personally I don't think it is even worth thinking about the second point - As in the end there is the user not having a very helpful feature because of some mark behavior he probably would not care at all (e.g. I never had the need for marking a fixture myself). Although of course it could be that just the implementation of the first point could prove too complicated itself but I guess it is always the case with new features. |
@Sup3rGeo I'm confused about you referring to |
Not directly related, but connected: I have created a plugin to design higher-level marks for filtering. It is named pytest-pilot. This is maybe another example of the huge gap that @Sup3rGeo pointed out, between parametrization marks (that are closely related to the core pytest engine itself) and other marks (that, as demonstrated by this extension, are quite "far" from the core of pytest and can be manipulated easily without hacking into the pytest engine. No new debate here, just food for thoughts :) . |
Miniconda Python 3.6.6, pytest 3.8.0 from conda-forge
@pytest.fixture(params)
does not allow me to parameterize one fixture with multiple orthogonal parameters (I want all 4 combinations ofpath
andcfg_factory
).I find the
pytest.fixture(params=[...])
to be extremely inelegant and confusing compared to@pytest.mark.parametrize
. This is because of the requirement that the function argument be namedrequest
, the actual parameter be namedrequest.param
(making the parameter name non-self-documenting and confusing at first).maybe test functions can accept parametric parameters directly, while fixture functions can only accept fixtures as parameters? if so, that's a problem that should be solved.
Workaround: Boilerplate fixtures which wrap lists:
But this adds many boilerplate lines of code. I thought pytest was a boilerplate-free test system.
The text was updated successfully, but these errors were encountered: