15

I have a fixture that creates a list of items during tests. I want to have another fixture which is parametrized with values generated by the first one.

Example code

import random
import pytest

@pytest.fixture
def values():
    return [random.randint(0, 100) for _ in range(10)]


@pytest.fixture
def value(request):
    return request.param


@pytest.mark.parametrize("value", params=values):
def test_function(value):
    assert value > 0

The problem with above code is that values is a function and not a list. I did quite a lot of digging but didnt find any way to unpack fixture to parametrize another with it.

Im aware that i can pass values fixture and iterate over it in tests, but that not a good solution since i want to see which values cause test to fail.

Im also open to alternative solutions, for example if it is possible to run subtests from started test.

festinuz
  • 153
  • 1
  • 6
  • 4
    Unfortunately, it is not possible to use fixtures as parametrization source, see [this issue](https://github.com/pytest-dev/pytest/issues/349). There exists a plugin that implements this feature: [`pytest-lazy-fixture`](https://github.com/TvoroG/pytest-lazy-fixture), you can try that out if you want. However, in your particular example, the `values` fixture has no args and can be easily used as a plain function, so you can just mark the test with `@pytest.mark.parametrize('value', values())`. – hoefling May 08 '18 at 11:19
  • 1
    Could you define `values` as a module-level variable - does it really need to be a fixture? – Tom Dalton May 08 '18 at 12:16
  • 1
    Thanks for suggestions. Unfortunately, i oversimplified example. In my case `values` fixture uses some other fixtures defined above it. To make it module-level variable i would have to make all fixtures that it depends on into variables as well. As for `pytest-lazy-fixture`, it allows to use fixtures as params but not as source for params - you cant do `parametrize("x", params=lazy_fixture(values))` – festinuz May 08 '18 at 17:59

1 Answers1

21

This seems like a misunderstanding of the concept of fixtures and its difference with the concept of parameters.

Pytest has two major phases:

  • a collection phase where the goal is to create a list of test "nodes" to run. One test "node" corresponds to one test id, and means one value for each parameter. In this phase fixtures are NOT executed, only the decorator marks (containing parameters) are read. Therefore only parameters declared in the decorators can influence this phase.

  • an execution phase where each test node is run. Before the run, all required fixtures that are not already setup are setup. Therefore the fixture functions are executed in this phase, and only in this phase. Their results can not modify the list of tests already done in the previous phase. (in other words, no new test node can be added dynamically through fixtures).

In your example, you want the result of a fixture setup (phase B) to change the list of tests to create (phase A): this is not possible by design. You have to create this list somewhere else, for example in a pytest init hook in a conftest.py or simply as a shared variable in any of your test modules, and refer to it in the parameters of your test or fixture.

See also this question that is quite similar: Parametrizing tests depending of also parametrized values in pytest

Note that to complement hoefling's comment to your question you can now use a parametrized fixture in a parameters list: I have added this feature in my pytest-cases plugin, for evaluation, so that we can eventually propose to merge it within pytest (see this discussion, so not hesitate to provide feedback!). But unfortunately this will not solve the precise problem you describe in this post, for the fundamental reason described above.

smarie
  • 4,568
  • 24
  • 39
  • Why is your plugin still not in pytest? It seems so fundamental. – Mad Physicist Sep 02 '21 at 01:32
  • Thanks @MadPhysicist :) Well these features require quite a number of non-trivial internal engine changes so this calls for a major refactoring in pytest. From what I understand, pytest core team is already spending a lot of dev time on other topics (including cleaning and bugfixes) so it does not seem to be the right time for them yet. Hopefully soon! – smarie Sep 02 '21 at 07:20
  • I hope so too. I was a little disappointed when I learned that the test cases were precomputed instead of being treated as a proper graph. – Mad Physicist Sep 02 '21 at 07:23
  • You are right, the only graph that exists in pytest is the fixtures' graph (already quite complex though, because of the multi-scopes mechanism). They handle parameters as something that is precomputed. – smarie Sep 02 '21 at 09:46
  • [smaire][1], my problem is that I want to parameterize a fixture with comma-separated values passed as a custom command line argument I added. So I need to parameterize a fixture (not test) with another fixture. Will this come under phase B. [1]: https://stackoverflow.com/users/7262247/smarie – Sandeep Dharembra Jan 10 '23 at 15:32