test_ok2/doc/en/funcargs.txt

263 lines
9.9 KiB
Plaintext
Raw Normal View History

==============================================================
Injecting objects into test functions (funcargs)
==============================================================
.. currentmodule:: _pytest.python
2010-11-06 06:37:25 +08:00
.. _`funcargs`:
.. _`funcarg mechanism`:
Dependency injection through function arguments
=================================================
.. note::
This section describes the pytest mechanisms prior
to the pytest-2.3 release. If you haven't used these
features yet, it makes more sense to stop here and read
:ref:`resources` instead.
py.test lets you inject objects into test invocations and precisely
control their life cycle in relation to the overall test execution. Moreover,
you can run a test function multiple times injecting different objects.
The basic mechanism for injecting objects is also called the
*funcarg mechanism* because objects are ultimately injected
by calling a test function with it as an argument. Unlike the
classical xUnit approach *funcargs* relate more to `Dependency Injection`_
because they help to de-couple test code from objects required for
them to execute. At test writing time you do not need to care for the
details of how your required resources are constructed or if they
live through a function, class, module or session scope.
.. _`Dependency injection`: http://en.wikipedia.org/wiki/Dependency_injection
To create a value with which to call a test function a factory function
is called which gets full access to the test function context and can
register finalizers or invoke lifecycle-caching helpers. The factory
can be implemented in same test class or test module, in a
per-directory ``conftest.py`` file or in an external plugin. This
allows total de-coupling of test and setup code.
A test function may be invoked multiple times in which case we
speak of :ref:`parametrized testing <parametrizing-tests>`. This can be
very useful if you want to test e.g. against different database backends
or with multiple numerical arguments sets and want to reuse the same set
of test functions.
py.test comes with some :ref:`builtinresources` and there are some refined usages in the examples section.
.. _funcarg:
Basic injection example
--------------------------------
Let's look at a simple self-contained test module::
# content of ./test_simplefactory.py
def pytest_funcarg__myfuncarg(request):
return 42
def test_function(myfuncarg):
assert myfuncarg == 17
This test function needs an injected object named ``myfuncarg``.
py.test will automatically discover and call the ``pytest_funcarg__myfuncarg``
factory. Running the test looks like this::
$ py.test test_simplefactory.py
=========================== test session starts ============================
2012-07-16 16:47:41 +08:00
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2
plugins: xdist, bugzilla, cache, oejskit, pep8, cov
2010-11-26 20:26:56 +08:00
collecting ... collected 1 items
test_simplefactory.py F
================================= FAILURES =================================
______________________________ test_function _______________________________
myfuncarg = 42
def test_function(myfuncarg):
> assert myfuncarg == 17
E assert 42 == 17
test_simplefactory.py:5: AssertionError
2012-07-16 16:47:41 +08:00
========================= 1 failed in 0.02 seconds =========================
This shows that the test function was called with a ``myfuncarg``
argument value of ``42`` and the assert fails as expected. Here is
how py.test comes to call the test function this way:
1. py.test :ref:`finds <test discovery>` the ``test_function`` because
of the ``test_`` prefix. The test function needs a function argument
named ``myfuncarg``. A matching factory function is discovered by
looking for the name ``pytest_funcarg__myfuncarg``.
2. ``pytest_funcarg__myfuncarg(request)`` is called and
returns the value for ``myfuncarg``.
3. the test function can now be called: ``test_function(42)``.
This results in the above exception because of the assertion
mismatch.
Note that if you misspell a function argument or want
to use one that isn't available, you'll see an error
with a list of available function arguments.
.. Note::
You can always issue::
py.test --funcargs test_simplefactory.py
to see available function arguments.
The request object passed to factories
-----------------------------------------
Each funcarg factory receives a :py:class:`~_pytest.python.FuncargRequest` object which
provides methods to manage caching and finalization in the context of the
test invocation as well as several attributes of the the underlying test item. In fact, as of version pytest-2.3, the request API is implemented on all Item
objects and therefore the request object has general :py:class:`Node attributes and methods <_pytest.main.Node>` attributes. This is a backward compatible
change so no changes are neccessary for pre-2.3 funcarg factories.
2012-07-16 17:11:26 +08:00
.. _`parametrizing tests, generalized`: http://tetamap.wordpress.com/2009/05/13/parametrizing-python-tests-generalized/
.. _`blog post about the monkeypatch funcarg`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/
.. _`xUnit style`: xunit_setup.html
.. _`funcarg factory`:
.. _factory:
.. _`test generators`:
.. _`parametrizing-tests`:
.. _`parametrized test functions`:
Parametrizing multiple calls to a test function
===========================================================
You can parametrize multiple runs of the same test
function by adding new test function calls with different
function argument values. Let's look at a simple self-contained
example:
Basic generated test example
----------------------------
2010-11-06 06:37:25 +08:00
Let's consider a test module which uses the ``pytest_generate_tests``
hook to generate several calls to the same test function::
# content of test_example.py
def pytest_generate_tests(metafunc):
if "numiter" in metafunc.funcargnames:
metafunc.parametrize("numiter", range(10))
def test_func(numiter):
assert numiter < 9
Running this will generate ten invocations of ``test_func`` passing in each of the items in the list of ``range(10)``::
$ py.test test_example.py
=========================== test session starts ============================
2012-07-16 16:47:41 +08:00
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2
plugins: xdist, bugzilla, cache, oejskit, pep8, cov
2010-11-26 20:26:56 +08:00
collecting ... collected 10 items
test_example.py .........F
================================= FAILURES =================================
_______________________________ test_func[9] _______________________________
numiter = 9
def test_func(numiter):
> assert numiter < 9
E assert 9 < 9
test_example.py:6: AssertionError
2012-07-16 16:47:41 +08:00
==================== 1 failed, 9 passed in 0.03 seconds ====================
Obviously, only when ``numiter`` has the value of ``9`` does the test fail. Note that the ``pytest_generate_tests(metafunc)`` hook is called during
2010-11-06 06:37:25 +08:00
the test collection phase which is separate from the actual test running.
Let's just look at what is collected::
$ py.test --collectonly test_example.py
=========================== test session starts ============================
2012-07-16 16:47:41 +08:00
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2
plugins: xdist, bugzilla, cache, oejskit, pep8, cov
collecting ... collected 10 items
<Module 'test_example.py'>
<Function 'test_func[0]'>
<Function 'test_func[1]'>
<Function 'test_func[2]'>
<Function 'test_func[3]'>
<Function 'test_func[4]'>
<Function 'test_func[5]'>
<Function 'test_func[6]'>
<Function 'test_func[7]'>
<Function 'test_func[8]'>
<Function 'test_func[9]'>
2012-07-16 16:47:41 +08:00
============================= in 0.02 seconds =============================
If you want to select only the run with the value ``7`` you could do::
$ py.test -v -k 7 test_example.py # or -k test_func[7]
=========================== test session starts ============================
2012-07-16 16:47:41 +08:00
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2 -- /home/hpk/venv/1/bin/python
cachedir: /home/hpk/tmp/doc-exec-271/.cache
plugins: xdist, bugzilla, cache, oejskit, pep8, cov
2010-11-26 20:26:56 +08:00
collecting ... collected 10 items
2012-07-16 16:47:41 +08:00
test_example.py:5: test_func[0] PASSED
test_example.py:5: test_func[1] PASSED
test_example.py:5: test_func[2] PASSED
test_example.py:5: test_func[3] PASSED
test_example.py:5: test_func[4] PASSED
test_example.py:5: test_func[5] PASSED
test_example.py:5: test_func[6] PASSED
test_example.py:5: test_func[7] PASSED
2012-07-16 16:47:41 +08:00
test_example.py:5: test_func[8] PASSED
test_example.py:5: test_func[9] FAILED
2012-07-16 16:47:41 +08:00
================================= FAILURES =================================
_______________________________ test_func[9] _______________________________
numiter = 9
def test_func(numiter):
> assert numiter < 9
E assert 9 < 9
test_example.py:6: AssertionError
==================== 1 failed, 9 passed in 0.03 seconds ====================
You might want to look at :ref:`more parametrization examples <paramexamples>`.
.. _`metafunc object`:
The **metafunc** object
-------------------------------------------
metafunc objects are passed to the ``pytest_generate_tests`` hook.
They help to inspect a testfunction and to generate tests
according to test configuration or values specified
in the class or module where a test function is defined:
``metafunc.funcargnames``: set of required function arguments for given function
``metafunc.function``: underlying python test function
``metafunc.cls``: class object where the test function is defined in or None.
``metafunc.module``: the module object where the test function is defined in.
``metafunc.config``: access to command line opts and general config
2011-11-20 07:45:05 +08:00
.. automethod:: Metafunc.parametrize
.. automethod:: Metafunc.addcall(funcargs=None,id=_notexists,param=_notexists)