5

Using Pytest, I want to write a test function which takes in multiple fixtures as arguments. Each of the fixtures has several parameters.

For example, in test_demo.py is a function test_squared_is_less_than_10 that takes the fixtures, negative_integer and positive_integer as arguments, and then checks that the squared of the fixture parameters is less than 10.

import pytest

@pytest.fixture(params=[-1, -2, -3])
def negative_integer(request):
    return request.param

@pytest.fixture(params=[1, 2, 3])
def positive_integer(request):
    return request.param

def test_squared_is_less_than_10(negative_integer, positive_integer):
    assert negative_integer ** 2 < 10
    assert positive_integer ** 2 < 10

I expect that when I run the pytest command in the terminal, a total of 6 tests should be executed, i.e. [-1, -2, -3] for positive_integer and [1, 2, 3] for negative_integer.

However, pytest seems to executing a nested loop kind of iteration so that 9 tests are executed i.e. [(-1, 1), (-1, 2), (-1, 3), (-2, 1), (-2, 2), (-2, 3), (-3, 1), (-3, 2), (-3, 3)].

Here is the output when I run pytest -v -k "test_squared_is_less":

test_squared_is_less_than_10[-1-1] PASSED                                                              
test_squared_is_less_than_10[-1-2] PASSED                                                              
test_squared_is_less_than_10[-1-3] PASSED                                                              
test_squared_is_less_than_10[-2-1] PASSED                                                              
test_squared_is_less_than_10[-2-2] PASSED                                                              
test_squared_is_less_than_10[-2-3] PASSED                                                              
test_squared_is_less_than_10[-3-1] PASSED                                                              
test_squared_is_less_than_10[-3-2] PASSED                                                              
test_squared_is_less_than_10[-3-3] PASSED 

This is undesirable, as I only want to carry out 6 tests instead of 9. With a larger number of params (say 20), pytest will execute 400 tests instead of the desired 40 tests, which is a waste of computational time.

How can I deal with this problem.

P.S: I would like to avoid writing two separate tests, such as

test_negative_squared_is_less_than_10(negative_integer) and test_positive_squared_is_less_than_10(positive_integer)

ProteinGuy
  • 1,754
  • 2
  • 17
  • 33

1 Answers1

6

You can apply non-cartesian parametrization via the @pytest.mark.parametrize marker. Your code, refactored:

import pytest


neg_params = [-1, -2, -3]

@pytest.fixture(params=neg_params)
def negative_integer(request):
    return request.param


pos_params = [1, 2, 3]

@pytest.fixture(params=pos_params)
def positive_integer(request):
    return request.param


@pytest.mark.parametrize(
    "negative_integer, positive_integer",
    zip(neg_params, pos_params),
    indirect=True
)
def test_squared_is_less_than_10(negative_integer, positive_integer):
    assert negative_integer ** 2 < 10
    assert positive_integer ** 2 < 10
hoefling
  • 59,418
  • 12
  • 147
  • 194
  • Thanks @hoefling. It worked! I can't seem to find proper documentation for pytest fixtures that explains all the arguments (such as `indirect` which you used in your code). Do you know where I can find it? – ProteinGuy Mar 27 '20 at 16:52
  • @JafetVoltron glad I could help! The relevant bits are somewhat scattered, but [Parametrizing fixtures and test functions](http://doc.pytest.org/en/latest/parametrize.html) and [Parametrizing tests](http://doc.pytest.org/en/latest/example/parametrize.html) have the examples, see esp. [Deferring the setup of parametrized resources](http://doc.pytest.org/en/latest/example/parametrize.html#deferring-the-setup-of-parametrized-resources). Another good source with more examples of `indirect` is [hackebrot's pytest-tricks](https://hackebrot.github.io/pytest-tricks/mark_parametrize_with_indirect/). – hoefling Mar 27 '20 at 17:16