============================================================== Injecting objects into test functions (funcargs) ============================================================== .. currentmodule:: _pytest.python .. _`funcargs`: .. _`funcarg mechanism`: Dependency injection through function arguments ================================================= py.test lets you inject objects into test functions and precisely control their life cycle in relation to the test execution. It is also possible to run a test function multiple times with 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. .. _`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, or in a per-directory ``conftest.py`` file or even in an external plugin. This allows full de-coupling of test code and objects needed for test execution. A test function may be invoked multiple times in which case we speak of :ref:`parametrized testing `. 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 :ref:`builtinfuncargs` 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 discover and call the factory named ``pytest_funcarg__myfuncarg`` within the same module in this case. Running the test looks like this:: $ py.test test_simplefactory.py =========================== test session starts ============================ platform linux2 -- Python 2.7.1 -- pytest-2.2.4 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.01 seconds ========================= This means that indeed the test function was called with a ``myfuncarg`` argument value of ``42`` and the assert fails. Here is how py.test comes to call the test function this way: 1. py.test :ref:`finds ` 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. You can always 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 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 ---------------------------- 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 ============================ platform linux2 -- Python 2.7.1 -- pytest-2.2.4 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 ==================== 1 failed, 9 passed in 0.02 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 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 ============================ platform linux2 -- Python 2.7.1 -- pytest-2.2.4 collecting ... collected 10 items ============================= in 0.00 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 ============================ platform linux2 -- Python 2.7.1 -- pytest-2.2.4 -- /home/hpk/venv/0/bin/python collecting ... collected 10 items test_example.py:5: test_func[7] PASSED ======================= 9 tests deselected by '-k7' ======================== ================== 1 passed, 9 deselected in 0.01 seconds ================== You might want to look at :ref:`more parametrization examples `. .. _`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.parametrize .. automethod:: Metafunc.addcall(funcargs=None,id=_notexists,param=_notexists)