allow to specify parametrize inputs as a comma-separated string

add Wouter to changelog and to authors
This commit is contained in:
holger krekel 2013-05-28 10:32:54 +02:00
parent bc5a5a63f2
commit c294a417bd
7 changed files with 71 additions and 27 deletions

View File

@ -7,6 +7,7 @@ Ronny Pfannschmidt
Benjamin Peterson Benjamin Peterson
Floris Bruynooghe Floris Bruynooghe
Jason R. Coombs Jason R. Coombs
Wouter van Ackooy
Samuele Pedroni Samuele Pedroni
Brianna Laugher Brianna Laugher
Carl Friedrich Bolz Carl Friedrich Bolz

View File

@ -4,6 +4,13 @@ Changes between 2.3.5 and 2.4.DEV
- fix issue 308 - allow to mark/xfail/skip individual parameter sets - fix issue 308 - allow to mark/xfail/skip individual parameter sets
when parametrizing. Thanks Brianna Laugher. when parametrizing. Thanks Brianna Laugher.
- simplify parametrize() signature: allow to pass a CSV-separated string
to specify argnames. For example: ``pytest.mark.parametrize("input,expected", [(1,2), (2,3)])`` is possible now in addition to the prior
``pytest.mark.parametrize(("input", "expected"), ...)``.
- fix issue 306 - cleanup of -k/-m options to only match markers/test
names/keywords respectively. Thanks Wouter van Ackooy.
- (experimental) allow fixture functions to be - (experimental) allow fixture functions to be
implemented as context managers. Thanks Andreas Pelme, implemented as context managers. Thanks Andreas Pelme,
Vladimir Keleshev. Vladimir Keleshev.

View File

@ -1,2 +1,2 @@
# #
__version__ = '2.4.0.dev2' __version__ = '2.4.0.dev3'

View File

@ -649,7 +649,8 @@ class Metafunc(FuncargnamesCompatAttr):
during the collection phase. If you need to setup expensive resources during the collection phase. If you need to setup expensive resources
see about setting indirect=True to do it rather at test setup time. see about setting indirect=True to do it rather at test setup time.
:arg argnames: an argument name or a list of argument names :arg argnames: a comma-separated string denoting one or more argument
names, or a list/tuple of argument strings.
:arg argvalues: The list of argvalues determines how often a :arg argvalues: The list of argvalues determines how often a
test is invoked with different argument values. If only one test is invoked with different argument values. If only one
@ -688,7 +689,8 @@ class Metafunc(FuncargnamesCompatAttr):
argvalues = unwrapped_argvalues argvalues = unwrapped_argvalues
if not isinstance(argnames, (tuple, list)): if not isinstance(argnames, (tuple, list)):
argnames = (argnames,) argnames = [x.strip() for x in argnames.split(",") if x.strip()]
if len(argnames) == 1:
argvalues = [(val,) for val in argvalues] argvalues = [(val,) for val in argvalues]
if not argvalues: if not argvalues:
argvalues = [(_notexists,) * len(argnames)] argvalues = [(_notexists,) * len(argnames)]

View File

@ -30,7 +30,7 @@ pytest supports test parametrization in several well-integrated ways:
.. regendoc: wipe .. regendoc: wipe
.. versionadded:: 2.2 .. versionadded:: 2.2, improved in 2.4
The builtin ``pytest.mark.parametrize`` decorator enables The builtin ``pytest.mark.parametrize`` decorator enables
parametrization of arguments for a test function. Here is a typical example parametrization of arguments for a test function. Here is a typical example
@ -39,7 +39,7 @@ to an expected output::
# content of test_expectation.py # content of test_expectation.py
import pytest import pytest
@pytest.mark.parametrize(("input", "expected"), [ @pytest.mark.parametrize("input,expected", [
("3+5", 8), ("3+5", 8),
("2+4", 6), ("2+4", 6),
("6*9", 42), ("6*9", 42),
@ -47,23 +47,24 @@ to an expected output::
def test_eval(input, expected): def test_eval(input, expected):
assert eval(input) == expected assert eval(input) == expected
Here, the ``@parametrize`` decorator defines three different argument Here, the ``@parametrize`` decorator defines three different ``(input,output)``
sets for the two ``(input, output)`` arguments of the ``test_eval`` function tuples so that that the ``test_eval`` function will run three times using
which will thus run three times:: them in turn::
$ py.test $ py.test
=========================== test session starts ============================ ============================= test session starts ==============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5 platform linux2 -- Python 2.7.3 -- pytest-2.4.0.dev3
plugins: xdist, cache, cli, pep8, xprocess, cov, capturelog, bdd-splinter, rerunfailures, instafail, localserver
collected 3 items collected 3 items
test_expectation.py ..F test_expectation.py ..F
================================= FAILURES ================================= =================================== FAILURES ===================================
____________________________ test_eval[6*9-42] _____________________________ ______________________________ test_eval[6*9-42] _______________________________
input = '6*9', expected = 42 input = '6*9', expected = 42
@pytest.mark.parametrize(("input", "expected"), [ @pytest.mark.parametrize("input,expected", [
("3+5", 8), ("3+5", 8),
("2+4", 6), ("2+4", 6),
("6*9", 42), ("6*9", 42),
@ -74,19 +75,21 @@ which will thus run three times::
E + where 54 = eval('6*9') E + where 54 = eval('6*9')
test_expectation.py:8: AssertionError test_expectation.py:8: AssertionError
==================== 1 failed, 2 passed in 0.01 seconds ==================== ====================== 1 failed, 2 passed in 0.02 seconds ======================
As expected only one pair of input/output values fails the simple test function. As designed in this example, only one pair of input/output values fails
And as usual with test function arguments, you can see the ``input`` and ``output`` values in the traceback. the simple test function. And as usual with test function arguments,
you can see the ``input`` and ``output`` values in the traceback.
Note that there ways how you can mark a class or a module, Note that you could also use the parametrize marker on a class or a module
see :ref:`mark`. (see :ref:`mark`) which would invoke several functions with the argument sets.
It is also possible to mark individual test instances within parametrize:: It is also possible to mark individual test instances within parametrize,
for example with the builtin ``mark.xfail``::
# content of test_expectation.py # content of test_expectation.py
import pytest import pytest
@pytest.mark.parametrize(("input", "expected"), [ @pytest.mark.parametrize("input,expected", [
("3+5", 8), ("3+5", 8),
("2+4", 6), ("2+4", 6),
pytest.mark.xfail(("6*9", 42)), pytest.mark.xfail(("6*9", 42)),
@ -94,6 +97,27 @@ It is also possible to mark individual test instances within parametrize::
def test_eval(input, expected): def test_eval(input, expected):
assert eval(input) == expected assert eval(input) == expected
Let's run this::
$ py.test
============================= test session starts ==============================
platform linux2 -- Python 2.7.3 -- pytest-2.4.0.dev3
plugins: xdist, cache, cli, pep8, xprocess, cov, capturelog, bdd-splinter, rerunfailures, instafail, localserver
collected 3 items
test_expectation.py ..x
===================== 2 passed, 1 xfailed in 0.02 seconds ======================
The one parameter set which caused a failure previously now
shows up as an "xfailed (expected to fail)" test.
.. note::
In versions prior to 2.4 one needed to specify the argument
names as a tuple. This remains valid but the simpler ``"name1,name2,..."``
comma-separated-string syntax is now advertised fist because
it's easier to write, produces less line noise.
.. _`pytest_generate_tests`: .. _`pytest_generate_tests`:
@ -140,15 +164,15 @@ Let's also run with a stringinput that will lead to a failing test::
$ py.test -q --stringinput="!" test_strings.py $ py.test -q --stringinput="!" test_strings.py
F F
================================= FAILURES ================================= =================================== FAILURES ===================================
___________________________ test_valid_string[!] ___________________________ _____________________________ test_valid_string[!] _____________________________
stringinput = '!' stringinput = '!'
def test_valid_string(stringinput): def test_valid_string(stringinput):
> assert stringinput.isalpha() > assert stringinput.isalpha()
E assert <built-in method isalpha of str object at 0x2ba729dab300>() E assert <built-in method isalpha of str object at 0x7fd657390fd0>()
E + where <built-in method isalpha of str object at 0x2ba729dab300> = '!'.isalpha E + where <built-in method isalpha of str object at 0x7fd657390fd0> = '!'.isalpha
test_strings.py:3: AssertionError test_strings.py:3: AssertionError
@ -160,8 +184,8 @@ listlist::
$ py.test -q -rs test_strings.py $ py.test -q -rs test_strings.py
s s
========================= short test summary info ========================== =========================== short test summary info ============================
SKIP [1] /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:974: got empty parameter set, function test_valid_string at /tmp/doc-exec-240/test_strings.py:1 SKIP [1] /home/hpk/p/pytest/_pytest/python.py:999: got empty parameter set, function test_valid_string at /tmp/doc-exec-2/test_strings.py:1
For further examples, you might want to look at :ref:`more For further examples, you might want to look at :ref:`more
parametrization examples <paramexamples>`. parametrization examples <paramexamples>`.

View File

@ -12,7 +12,7 @@ def main():
name='pytest', name='pytest',
description='py.test: simple powerful testing with Python', description='py.test: simple powerful testing with Python',
long_description = long_description, long_description = long_description,
version='2.4.0.dev2', version='2.4.0.dev3',
url='http://pytest.org', url='http://pytest.org',
license='MIT license', license='MIT license',
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],

View File

@ -221,6 +221,16 @@ class TestMetafunc:
"*6 fail*", "*6 fail*",
]) ])
def test_parametrize_CSV(self, testdir):
testdir.makepyfile("""
import pytest
@pytest.mark.parametrize("x, y,", [(1,2), (2,3)])
def test_func(x, y):
assert x+1 == y
""")
reprec = testdir.inline_run()
reprec.assertoutcome(passed=2)
def test_parametrize_class_scenarios(self, testdir): def test_parametrize_class_scenarios(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
# same as doc/en/example/parametrize scenario example # same as doc/en/example/parametrize scenario example