2012-10-07 19:06:17 +08:00
.. _`test generators`:
.. _`parametrizing-tests`:
.. _`parametrized test functions`:
.. _`parametrize`:
2012-10-18 18:24:50 +08:00
.. _`parametrize-basics`:
2012-10-07 19:06:17 +08:00
Parametrizing fixtures and test functions
==========================================================================
2017-05-09 22:02:08 +08:00
pytest enables test parametrization at several levels:
2012-10-07 19:06:17 +08:00
2018-05-18 16:19:46 +08:00
- :py:func: `pytest.fixture` allows one to :ref:`parametrize fixture
2017-05-09 22:02:08 +08:00
functions <fixture-parametrize>`.
2012-10-08 19:19:31 +08:00
2018-05-18 16:19:46 +08:00
* `@pytest.mark.parametrize`_ allows one to define multiple sets of
2017-05-09 22:02:08 +08:00
arguments and fixtures at the test function or class.
2012-10-07 19:06:17 +08:00
2018-05-18 16:19:46 +08:00
* `pytest_generate_tests`_ allows one to define custom parametrization
2017-05-09 22:02:08 +08:00
schemes or extensions.
2012-10-07 19:06:17 +08:00
2012-10-18 18:24:50 +08:00
.. _parametrizemark:
2012-10-07 19:06:17 +08:00
.. _`@pytest.mark.parametrize`:
2012-10-09 20:35:17 +08:00
2012-10-07 19:06:17 +08:00
`` @pytest.mark.parametrize `` : parametrizing test functions
---------------------------------------------------------------------
.. regendoc: wipe
2019-04-28 23:37:58 +08:00
2015-07-10 08:50:38 +08:00
Several improvements.
2012-10-07 19:06:17 +08:00
2018-03-01 07:34:20 +08:00
The builtin :ref: `pytest.mark.parametrize ref` decorator enables
2012-10-07 19:06:17 +08:00
parametrization of arguments for a test function. Here is a typical example
2012-10-08 19:19:31 +08:00
of a test function that implements checking that a certain input leads
2019-04-12 19:50:26 +08:00
to an expected output:
.. code-block :: python
2012-10-07 19:06:17 +08:00
# content of test_expectation.py
import pytest
2019-04-12 19:50:26 +08:00
@pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
2016-01-13 09:01:34 +08:00
def test_eval(test_input, expected):
assert eval(test_input) == expected
2012-10-07 19:06:17 +08:00
2016-01-13 09:01:34 +08:00
Here, the `` @parametrize `` decorator defines three different `` (test_input,expected) ``
2014-01-10 05:27:23 +08:00
tuples so that the `` test_eval `` function will run three times using
2018-11-24 13:41:22 +08:00
them in turn:
.. code-block :: pytest
2012-10-07 19:06:17 +08:00
2016-06-21 22:16:57 +08:00
$ pytest
2017-11-23 23:33:41 +08:00
=========================== test session starts ============================
2019-07-05 08:01:16 +08:00
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
2019-01-31 00:25:38 +08:00
cachedir: $PYTHON_PREFIX/.pytest_cache
2019-04-15 22:24:17 +08:00
rootdir: $REGENDOC_TMPDIR
2012-10-09 20:35:17 +08:00
collected 3 items
2018-05-18 16:19:46 +08:00
2017-11-23 23:33:41 +08:00
test_expectation.py ..F [100%]
2018-05-18 16:19:46 +08:00
2017-11-23 23:33:41 +08:00
================================= FAILURES =================================
____________________________ test_eval[6*9-42] _____________________________
2018-05-18 16:19:46 +08:00
2016-01-13 09:01:34 +08:00
test_input = '6*9', expected = 42
2018-05-18 16:19:46 +08:00
2019-04-15 22:24:17 +08:00
@pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
2016-01-13 09:01:34 +08:00
def test_eval(test_input, expected):
> assert eval(test_input) == expected
2017-05-13 04:17:40 +08:00
E AssertionError: assert 54 == 42
2012-10-07 19:06:17 +08:00
E + where 54 = eval('6*9')
2018-05-18 16:19:46 +08:00
2019-04-15 22:24:17 +08:00
test_expectation.py:6: AssertionError
2020-03-11 22:23:25 +08:00
========================= short test summary info ==========================
FAILED test_expectation.py::test_eval[6*9-42] - AssertionError: assert 54...
2019-08-30 23:43:47 +08:00
======================= 1 failed, 2 passed in 0.12s ========================
2012-10-07 19:06:17 +08:00
2017-10-12 00:11:50 +08:00
.. note ::
pytest by default escapes any non-ascii characters used in unicode strings
for the parametrization because it has several downsides.
2019-03-27 23:05:33 +08:00
If however you would like to use unicode strings in parametrization and see them in the terminal as is (non-escaped), use this option in your `` pytest.ini `` :
2017-10-12 00:11:50 +08:00
.. code-block :: ini
[pytest]
disable_test_id_escaping_and_forfeit_all_rights_to_community_support = True
2019-03-27 23:05:33 +08:00
Keep in mind however that this might cause unwanted side effects and
2017-10-12 00:11:50 +08:00
even bugs depending on the OS used and plugins currently installed, so use it at your own risk.
2013-05-28 16:32:54 +08:00
As designed in this example, only one pair of input/output values fails
the simple test function. And as usual with test function arguments,
you can see the `` input `` and `` output `` values in the traceback.
2012-10-07 19:06:17 +08:00
2013-05-28 16:32:54 +08:00
Note that you could also use the parametrize marker on a class or a module
(see :ref: `mark` ) which would invoke several functions with the argument sets.
2012-10-07 19:06:17 +08:00
2013-05-28 16:32:54 +08:00
It is also possible to mark individual test instances within parametrize,
2019-04-12 19:50:26 +08:00
for example with the builtin `` mark.xfail `` :
.. code-block :: python
2013-05-21 09:12:45 +08:00
# content of test_expectation.py
import pytest
2019-04-12 19:50:26 +08:00
@pytest.mark.parametrize(
"test_input,expected",
[("3+5", 8), ("2+4", 6), pytest.param("6*9", 42, marks=pytest.mark.xfail)],
)
2016-01-13 09:01:34 +08:00
def test_eval(test_input, expected):
assert eval(test_input) == expected
2013-05-21 09:12:45 +08:00
2018-11-24 13:41:22 +08:00
Let's run this:
.. code-block :: pytest
2013-05-28 16:32:54 +08:00
2016-06-21 22:16:57 +08:00
$ pytest
2017-11-23 23:33:41 +08:00
=========================== test session starts ============================
2019-07-05 08:01:16 +08:00
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
2019-01-31 00:25:38 +08:00
cachedir: $PYTHON_PREFIX/.pytest_cache
2019-04-15 22:24:17 +08:00
rootdir: $REGENDOC_TMPDIR
2013-05-28 16:32:54 +08:00
collected 3 items
2018-05-18 16:19:46 +08:00
2017-11-23 23:33:41 +08:00
test_expectation.py ..x [100%]
2018-05-18 16:19:46 +08:00
2019-08-30 23:43:47 +08:00
======================= 2 passed, 1 xfailed in 0.12s =======================
2013-05-28 16:32:54 +08:00
The one parameter set which caused a failure previously now
2020-06-11 16:22:47 +08:00
shows up as an "xfailed" (expected to fail) test.
2013-05-28 16:32:54 +08:00
2018-10-24 03:44:30 +08:00
In case the values provided to `` parametrize `` result in an empty list - for
example, if they're dynamically generated by some function - the behaviour of
pytest is defined by the :confval: `empty_parameter_set_mark` option.
2015-07-26 20:39:13 +08:00
To get all combinations of multiple parametrized arguments you can stack
2019-04-12 19:50:26 +08:00
`` parametrize `` decorators:
.. code-block :: python
2015-07-26 20:39:13 +08:00
import pytest
2019-04-12 19:50:26 +08:00
2015-07-26 20:39:13 +08:00
@pytest.mark.parametrize("x", [0, 1])
@pytest.mark.parametrize("y", [2, 3])
def test_foo(x, y):
pass
2018-05-18 16:19:46 +08:00
This will run the test with the arguments set to `` x=0/y=2 `` , `` x=1/y=2 `` ,
2017-12-16 23:31:48 +08:00
`` x=0/y=3 `` , and `` x=1/y=3 `` exhausting parameters in the order of the decorators.
2017-12-16 22:25:02 +08:00
2012-10-07 19:06:17 +08:00
.. _`pytest_generate_tests`:
Basic `` pytest_generate_tests `` example
---------------------------------------------
2012-10-08 19:19:31 +08:00
Sometimes you may want to implement your own parametrization scheme
or implement some dynamism for determining the parameters or scope
2014-01-18 19:31:33 +08:00
of a fixture. For this, you can use the `` pytest_generate_tests `` hook
2012-10-09 20:35:17 +08:00
which is called when collecting a test function. Through the passed in
2016-02-16 06:19:07 +08:00
`` metafunc `` object you can inspect the requesting test context and, most
2012-10-09 20:35:17 +08:00
importantly, you can call `` metafunc.parametrize() `` to cause
2014-01-18 19:31:33 +08:00
parametrization.
2012-10-09 20:35:17 +08:00
For example, let's say we want to run a test taking string inputs which
2014-01-18 19:31:33 +08:00
we want to set via a new `` pytest `` command line option. Let's first write
2019-04-12 19:50:26 +08:00
a simple test accepting a `` stringinput `` fixture function argument:
.. code-block :: python
2012-10-08 19:19:31 +08:00
# content of test_strings.py
2019-04-12 19:50:26 +08:00
2012-10-08 19:19:31 +08:00
def test_valid_string(stringinput):
assert stringinput.isalpha()
2012-10-07 19:06:17 +08:00
2014-01-18 19:31:33 +08:00
Now we add a `` conftest.py `` file containing the addition of a
2019-04-12 19:50:26 +08:00
command line option and the parametrization of our test function:
.. code-block :: python
2012-10-07 19:06:17 +08:00
# content of conftest.py
2019-04-12 19:50:26 +08:00
2012-10-07 19:06:17 +08:00
def pytest_addoption(parser):
2019-04-12 19:50:26 +08:00
parser.addoption(
"--stringinput",
action="append",
default=[],
help="list of stringinputs to pass to test functions",
)
2012-10-07 19:06:17 +08:00
def pytest_generate_tests(metafunc):
2019-04-12 19:50:26 +08:00
if "stringinput" in metafunc.fixturenames:
metafunc.parametrize("stringinput", metafunc.config.getoption("stringinput"))
2012-10-07 19:06:17 +08:00
2019-02-15 21:10:37 +08:00
If we now pass two stringinput values, our test will run twice:
.. code-block :: pytest
2012-10-07 19:06:17 +08:00
2016-06-21 22:16:57 +08:00
$ pytest -q --stringinput="hello" --stringinput="world" test_strings.py
2017-11-23 23:33:41 +08:00
.. [100%]
2019-09-18 21:11:59 +08:00
2 passed in 0.12s
2012-10-07 19:06:17 +08:00
2018-11-24 13:41:22 +08:00
Let's also run with a stringinput that will lead to a failing test:
.. code-block :: pytest
2012-10-07 19:06:17 +08:00
2016-06-21 22:16:57 +08:00
$ pytest -q --stringinput="!" test_strings.py
2017-11-23 23:33:41 +08:00
F [100%]
================================= FAILURES =================================
___________________________ test_valid_string[!] ___________________________
2018-05-18 16:19:46 +08:00
2012-10-08 19:19:31 +08:00
stringinput = '!'
2018-05-18 16:19:46 +08:00
2012-10-08 19:19:31 +08:00
def test_valid_string(stringinput):
> assert stringinput.isalpha()
2017-05-13 04:17:40 +08:00
E AssertionError: assert False
2016-08-02 02:46:34 +08:00
E + where False = <built-in method isalpha of str object at 0xdeadbeef>()
E + where <built-in method isalpha of str object at 0xdeadbeef> = '!'.isalpha
2018-05-18 16:19:46 +08:00
2019-04-15 22:24:17 +08:00
test_strings.py:4: AssertionError
2020-03-11 22:23:25 +08:00
========================= short test summary info ==========================
FAILED test_strings.py::test_valid_string[!] - AssertionError: assert False
2019-09-18 21:11:59 +08:00
1 failed in 0.12s
2012-10-07 19:06:17 +08:00
2014-01-18 19:31:33 +08:00
As expected our test function fails.
2012-10-09 20:35:17 +08:00
If you don't specify a stringinput it will be skipped because
`` metafunc.parametrize() `` will be called with an empty parameter
2018-11-24 13:41:22 +08:00
list:
.. code-block :: pytest
2012-10-09 20:35:17 +08:00
2016-06-21 22:16:57 +08:00
$ pytest -q -rs test_strings.py
2017-11-23 23:33:41 +08:00
s [100%]
========================= short test summary info ==========================
2019-04-15 22:24:17 +08:00
SKIPPED [1] test_strings.py: got empty parameter set ['stringinput'], function test_valid_string at $REGENDOC_TMPDIR/test_strings.py:2
2019-09-18 21:11:59 +08:00
1 skipped in 0.12s
2012-10-07 19:06:17 +08:00
2018-05-18 16:19:46 +08:00
Note that when calling `` metafunc.parametrize `` multiple times with different parameter sets, all parameter names across
2017-09-10 08:50:45 +08:00
those sets cannot be duplicated, otherwise an error will be raised.
2017-11-10 05:25:30 +08:00
More examples
-------------
2012-10-08 19:19:31 +08:00
For further examples, you might want to look at :ref:`more
parametrization examples <paramexamples>`.