mark.parametrize with indirect


Jul 10, 2016
Raphael Pierzina
fixture, hook, indirect, marker, parametrize


Defer setup code and invoke fixtures from @pytest.mark.parametrize with indirect.

Star Fork


In Create Tests via Parametrization we've learned how to use @pytest.mark.parametrize to generate a number of test cases for one test implementation.


You can pass a keyword argument named indirect to parametrize to change how its parameters are being passed to the underlying test function. It accepts either a boolean value or a list of strings that refer to pytest.fixure functions.


If you set indirect to False or omit the parameter altogether, pytest will treat the given parameters as is w/o any specialties.


All of the parameters are stored on the special request fixture. Other fixtures can then access it via request.param and modify the test parameter.

List of fixture names

You can choose to only set indirect for a subset of arguments, by passing a list to the keyword argument: indirect=['foo', 'bar'].

Example code

We define two classes that have a few fields to hold information. The tests will access the attributes to check for correctness of our code. For that we create a number of instances of the Sushi class with varying parameters, before we call its property is_vegetarian in the test to see if the Fooshi Bar offers a few vegetarian dishes.

# -*- coding: utf-8 -*-

class Restaurant:
    def __init__(self, name, location, menu=None):
        if not menu:
            raise ValueError = name
        self.location = location = menu

class Sushi:
    def __init__(self, name, ingredients=None):
        if not ingredients:
            raise ValueError = name
        self.ingredients = ingredients

    def __contains__(self, ingredient):
        return ingredient in self.ingredients

    def is_vegetarian(self):
        for ingredient in ['Crab', 'Salmon', 'Shrimp', 'Tuna']:
            if ingredient in self:
                return False
        return True


A fixture named fooshi_bar creates a Restaurant with a variety of dishes on the menu.

The fixture sushi creates instances based on a name and looking up ingredients from the session scoped recipes fixture when the test is being run. Since it is created with params it will not only yield one but many instances. pytest will then create a number of test items for each of the test function that uses it.

# -*- coding: utf-8 -*-

import pytest

from sushi import Restaurant, Sushi

def fooshi_bar():
    """Returns a Restaurant instance with a great menu."""
    return Restaurant(
        'Fooshi Bar',
        location='Buenos Aires',
            'Ebi Nigiri',
            'Kappa Maki',
            'Miso Soup',
            'Sake Nigiri',

def recipes():
    """Return a map from types of sushi to ingredients."""
    return {
        'California Roll': ['Rice', 'Cucumber', 'Avocado', 'Crab'],
        'Ebi Nigiri': ['Shrimp', 'Rice'],
        'Inarizushi': ['Fried tofu', 'Rice'],
        'Kappa Maki': ['Cucumber', 'Rice', 'Nori'],
        'Maguro Nigiri': ['Tuna', 'Rice', 'Nori'],
        'Sake Nigiri': ['Salmon', 'Rice', 'Nori'],
        'Tamagoyaki': ['Fried egg', 'Rice', 'Nori'],
        'Tsunamayo Maki': ['Tuna', 'Mayonnaise'],

    'California Roll',
    'Ebi Nigiri',
    'Kappa Maki',
    'Maguro Nigiri',
    'Sake Nigiri',
    'Tsunamayo Maki',
def sushi(recipes, request):
    """Create a Sushi instance based on recipes."""
    name = request.param
    return Sushi(name, ingredients=recipes[name])


We define two test functions that both use the sushi fixture. test_fooshi_serves_vegetarian_sushi also uses fooshi_bar as well as side_dish, which is dynamically created during collection phase via mark.parametrize.

Note how sushi is created with indirect=True. Unlike side_dish it will be passed on to the according fixture function which turns the name of a type of sushi to an actual instance as explained above.

# -*- coding: utf-8 -*-

import pytest

    ['Kappa Maki', 'Tamagoyaki', 'Inarizushi'],
    ['Edamame', 'Miso Soup'],
def test_fooshi_serves_vegetarian_sushi(fooshi_bar, sushi, side_dish):
    assert sushi.is_vegetarian
    assert in
    assert side_dish in

def test_sushi(sushi):
    assert sushi.ingredients

Test run

When running these tests we can see that test_sushi is run eight times as expected as its only fixture sushi is created with eight parameters.

On the other hand test_fooshi_serves_vegetarian_sushi is run six times combining one value for fooshi_bar, two values for side_dish and three values for sushi!

$ py.test[Edamame-Kappa Maki] PASSED[Edamame-Tamagoyaki] PASSED[Edamame-Inarizushi] PASSED[Miso Soup-Kappa Maki] PASSED[Miso Soup-Tamagoyaki] PASSED[Miso Soup-Inarizushi] PASSED[California Roll] PASSED[Ebi Nigiri] PASSED[Inarizushi] PASSED[Kappa Maki] PASSED[Maguro Nigiri] PASSED[Sake Nigiri] PASSED[Tamagoyaki] PASSED[Tsunamayo Maki] PASSED

========================== 14 passed in 0.02 seconds ==========================

Deferred loading of resources with hooks

Since test parametrization is performed at collection time, you might want to set up expensive resources only when the tests that use it are being run.

You can achieve this by using indirect and doing the hard work in fixtures rather than helper functions directly. This is also available from the pytest_generate_tests hook:

def pytest_generate_tests(metafunc):
    if 'sushi' in metafunc.fixturenames:
            ['Kappa Maki', 'Tamagoyaki', 'Inarizushi'],

For more information see the parametrize pytest docs.