How to share parametrized arguments across multiple test functions?

As pointed by Kammil, best approach is to have a Test class, as an alternative to Kammil's approach, is to have a class attribute with your test data, by doing this way, you can have test methods that do not use common_arg1 and common_arg2 in your class. But you still need to mark the tests, but you do not need to rewrite all the parametrize parameters, and if you change the values from the class attribute it is reflected in all tests.

This is particularly useful if you are going to have test methods in the same class that do not need common_arg1 and common_arg2.

Bear in mind that if you decorate the class all the methods will have to have common_arg1 and common_arg2 as parameters, if you try to write a method that do not reference them in the method's args list you will get a function uses no argument 'common_arg1' error.

So to avoid this you can do:

import pytest

class TestParametrized:

    common_args = ('common_arg1, common_arg2', [([0, 1], [2,3])])

    @pytest.mark.parametrize(*common_args)
    @pytest.mark.parametrize('a', [0, 1])
    def test_1(self, common_arg1, common_arg2, a):
        pass

    @pytest.mark.parametrize(*common_args)
    @pytest.mark.parametrize('b', [0, 1])
    def test_2(self, common_arg1, common_arg2, b):
        pass

    @pytest.mark.parametrize(*common_args)
    @pytest.mark.parametrize('x', [0, 1])
    def test_100(self, common_arg1, common_arg2, x):
        pass

    def test_1000(self):
        pass

Note that test_1000 will be successful and will not complain about not mentioning common_args1 and common_args2. You can also split common_args in 2 by writing two class attributes.


Another approach is to use a parametrized pytest fixture. For a single parametrization it is easy to use plain pytest:

import pytest

@pytest.fixture(params=[0, 1])
def common_arg1(request):
    return request.param

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

@pytest.mark.parametrize('a', [0, 1])
def test_1(common_arg1, common_arg2, a):
    pass

@pytest.mark.parametrize('b', [0, 1])
def test_2(common_arg1, common_arg2, b):
    pass

@pytest.mark.parametrize('x', [0, 1])
def test_100(common_arg1, common_arg2, x):
    pass

If you wish to write this in a more compact way you can use pytest_cases.param_fixture:

from pytest_cases import param_fixture

common_arg1 = param_fixture('common_arg1', [0, 1])
common_arg2 = param_fixture('common_arg2', [2, 3])

For multiple parametrization you may wish to use @pytest_cases.fixture in order to be able to use @pytest.mark.parametrize:

import pytest
from pytest_cases import fixture

@fixture
@pytest.mark.parametrize('a', [0, 1])
@pytest.mark.parametrize('b', [2, 3])
def common_arg_multiple(a, b):
    return a * b, a - b

See pytest-cases documentation for details. (I'm the author ;) )


You could move all test cases into one helper class and put common decorators on top of that class. Decorators that are specific to some tests (methods) can be applied directly on them. Check the example below.

import pytest


@pytest.mark.parametrize('common_arg1', [0, 1])
@pytest.mark.parametrize('common_arg2', [2, 3])
class TestParametrized:

    @pytest.mark.parametrize('a', [0, 1])
    def test_1(self, common_arg1, common_arg2, a):
        pass

    @pytest.mark.parametrize('b', [0, 1])
    def test_2(self, common_arg1, common_arg2, b):
        pass

    @pytest.mark.parametrize('x', [0, 1])
    def test_100(self, common_arg1, common_arg2, x):
        pass

Tags:

Pytest