quicklinks

home TOC/contents
install changelog
examples customize
issues[bb] contact

Table Of Contents

Previous topic

The writing and reporting of assertions in tests

Next topic

Extended xUnit style setup fixtures

Injecting objects into test functions (funcargs)

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.

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 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 Builtin function arguments and there are some refined usages in the examples section.

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 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”).

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:

class _pytest.python.FuncargRequest

A request for function arguments from a test function.

Note that there is an optional param attribute in case there was an invocation to metafunc.addcall(param=...). If no such call was done in a pytest_generate_tests hook, the attribute will not be present.

function

function object of the test invocation.

cls

class (can be None) where the test function was collected.

module

module where the test function was collected.

keywords

keywords of the test function item.

New in version 2.0.

config

the pytest config object associated with this request.

FuncargRequest.addfinalizer(finalizer)

add finalizer function to be called after test function finished execution.

FuncargRequest.cached_setup(setup, teardown=None, scope='module', extrakey=None)

Return a testing resource managed by setup & teardown calls. scope and extrakey determine when the teardown function will be called so that subsequent calls to setup would recreate the resource.

Parameters:
  • teardown – function receiving a previously setup resource.
  • setup – a no-argument function creating a resource.
  • scope – a string value out of function, class, module or session indicating the caching lifecycle of the resource.
  • extrakey – added to internal caching key of (funcargname, scope).
FuncargRequest.applymarker(marker)

Apply a marker to a single test function invocation. This method is useful if you don’t want to have a keyword/marker on all function invocations.

Parameters:marker – a _pytest.mark.MarkDecorator object created by a call to py.test.mark.NAME(...).
FuncargRequest.getfuncargvalue(argname)

Retrieve a function argument by name for this test function invocation. This allows one function argument factory to call another function argument factory. If there are two funcarg factories for the same test function argument the first factory may use getfuncargvalue to call the second one and do something additional with the resource.

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
<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]'>

=============================  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 more parametrization examples.

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

Metafunc.parametrize(argnames, argvalues, indirect=False, ids=None)[source]

Add new invocations to the underlying test function using the list of argvalues for the given argnames. Parametrization is performed during the collection phase. If you need to setup expensive resources you may pass indirect=True and implement a funcarg factory which can perform the expensive setup just before a test is actually run.

Parameters:
  • argnames – an argument name or a list of argument names
  • argvalues – a list of values for the argname or a list of tuples of values for the list of argument names.
  • indirect – if True each argvalue corresponding to an argument will be passed as request.param to its respective funcarg factory so that it can perform more expensive setups during the setup phase of a test rather than at collection time.
  • ids – list of string ids each corresponding to the argvalues so that they are part of the test id. If no ids are provided they will be generated automatically from the argvalues.
Metafunc.addcall(funcargs=None, id=_notexists, param=_notexists)[source]

(deprecated, use parametrize) Add a new call to the underlying test function during the collection phase of a test run. Note that request.addcall() is called during the test collection phase prior and independently to actual test execution. You should only use addcall() if you need to specify multiple arguments of a test function.

Parameters:
  • funcargs – argument keyword dictionary used when invoking the test function.
  • id – used for reporting and identification purposes. If you don’t supply an id an automatic unique id will be generated.
  • param – a parameter which will be exposed to a later funcarg factory invocation through the request.param attribute.