An optional parameter to the fixture decorator is params.

For each value in params, the fixture will be called with request.param filled in with that value.
Tests that use the fixture will be called once FOR EACH value in params.

Toy example

An example is in order here.

This first example is a silly one, but does show the mechanics, and the utility of both the -v flag and how well pytest deals with failures of parameterized tests.

import pytest

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

def test_not_2(some_data):
    assert some_data != 2

def test_less_than_4(some_data):
    assert some_data < 4

This should run both tests three times each, and fail “test_not_2” when 2 is passed in.
I’m using -v so we can see both the test name, and the parameter passed in.
And also --tb=no because some_dta != 2 is not really an interesting traceback.

$ pytest -v --tb=no test_params.py         
================== test session starts ==================
collected 6 items                                       

test_params.py::test_not_2[1] PASSED              [ 16%]
test_params.py::test_not_2[2] FAILED              [ 33%]
test_params.py::test_not_2[3] PASSED              [ 50%]
test_params.py::test_less_than_4[1] PASSED        [ 66%]
test_params.py::test_less_than_4[2] PASSED        [ 83%]
test_params.py::test_less_than_4[3] PASSED        [100%]

================ short test summary info ================
FAILED test_params.py::test_not_2[2] - assert 2 != 2
============== 1 failed, 5 passed in 0.05s ==============

I wanted to show you a failing example just to show that pytest continues the rest of the parameter values, even if one fails.

Scope effect on parametrization

In the above example, we used the default “function” scope.
With parametrization of fixtures, that means the first test ran through all values of the fixture, before moving on to the next test.

If we change to “module” scope, we’ll see a different order:

So change the code as follows:

@pytest.fixture(params=[1,2,3], scope="module")
def some_data(request):
    return request.param

Now, we run through each test using the fixture, then move on to the next value and repeat:

$ pytest -v --tb=no test_params.py 
================== test session starts ==================
collected 6 items                                       

test_params.py::test_not_2[1] PASSED              [ 16%]
test_params.py::test_less_than_4[1] PASSED        [ 33%]
test_params.py::test_not_2[2] FAILED              [ 50%]
test_params.py::test_less_than_4[2] PASSED        [ 66%]
test_params.py::test_not_2[3] PASSED              [ 83%]
test_params.py::test_less_than_4[3] PASSED        [100%]

================ short test summary info ================
FAILED test_params.py::test_not_2[2] - assert 2 != 2
============== 1 failed, 5 passed in 0.05s ==============

This is super handy if the setup/teardown of the fixture is doing some time consuming work per param value.