test_ok1/doc/funcargs.txt

220 lines
7.4 KiB
Plaintext
Raw Normal View History

==============================================================
creating and managing test function arguments
==============================================================
.. currentmodule:: _pytest.python
2010-11-06 06:37:25 +08:00
.. _`funcargs`:
.. _`funcarg mechanism`:
Dependency injection through function arguments
=================================================
py.test allows to inject values into test functions through the *funcarg
mechanism*: For each argument name in a test function signature a factory is
looked up and called to create the value. The factory can live in the
2010-12-07 22:25:25 +08:00
same test class, test module, in a per-directory ``conftest.py`` file or
in an external plugin. It has full access to the requesting test
function, can register finalizers and invoke lifecycle-caching
helpers. As can be expected from a systematic dependency
injection mechanism, this allows full de-coupling of resource and
fixture setup from test code, enabling more maintainable and
easy-to-modify test suites.
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.
Basic funcarg example
-----------------------
Let's look at a simple self-contained example that you can put
into a test module::
# content of ./test_simplefactory.py
def pytest_funcarg__myfuncarg(request):
return 42
def test_function(myfuncarg):
assert myfuncarg == 17
Running the test looks like this::
$ py.test test_simplefactory.py
=========================== test session starts ============================
2010-11-26 20:26:56 +08:00
platform linux2 -- Python 2.6.5 -- pytest-2.0.0
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
========================= 1 failed in 0.02 seconds =========================
This means that the test function was called with a ``myfuncarg`` value
of ``42`` and the assert fails accordingly. 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)``
and 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. You can
also issue::
py.test --funcargs test_simplefactory.py
to see available function arguments (which you can also
think of as "resources").
.. _`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:
The funcarg **request** object
=============================================
Each funcarg factory receives a **request** object which is tied to a
specific test function call. A request object is passed to a funcarg
factory and provides access to test configuration and context:
.. autoclass:: _pytest.python.FuncargRequest()
:members: function,cls,module,keywords,config
.. _`useful caching and finalization helpers`:
.. automethod:: FuncargRequest.addfinalizer
.. automethod:: FuncargRequest.cached_setup
.. automethod:: FuncargRequest.applymarker
.. automethod:: FuncargRequest.getfuncargvalue
.. _`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:
for i in range(10):
metafunc.addcall(funcargs=dict(numiter=i))
def test_func(numiter):
assert numiter < 9
Running this::
$ py.test test_example.py
=========================== test session starts ============================
2010-11-26 20:26:56 +08:00
platform linux2 -- Python 2.6.5 -- pytest-2.0.0
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:7: AssertionError
==================== 1 failed, 9 passed in 0.03 seconds ====================
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
<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]'>
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 ============================
2010-11-26 20:26:56 +08:00
platform linux2 -- Python 2.6.5 -- pytest-2.0.0 -- /home/hpk/venv/0/bin/python
collecting ... collected 10 items
test_example.py:6: test_func[7] PASSED
======================== 9 tests deselected by '7' =========================
================== 1 passed, 9 deselected in 0.01 seconds ==================
.. _`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
.. automethod:: Metafunc.addcall(funcargs=None, id=_notexists, param=_notexists)