refine documentation, move setup to own "setup" page and provide

some more examples. move old setup_module/... to xunit_old page.
This commit is contained in:
holger krekel 2012-08-02 12:07:54 +02:00
parent 5fd84c35dd
commit ae241a5071
4 changed files with 274 additions and 187 deletions

View File

@ -25,7 +25,9 @@ Welcome to pytest!
- **supports functional testing and complex test setups**
- (new in 2.3) :ref:`easy test resource management and generalized xUnit setup <resources>`
- (new in 2.3) :ref:`easy test resource management, scoping and
parametrization <resources>`
- (new in 2.3) :ref:`xunitsetup`.
- (new in 2.2) :ref:`durations`
- (much improved in 2.2) :ref:`marking and test selection <mark>`
- (improved in 2.2) :ref:`parametrized test functions <parametrized test functions>`

View File

@ -1,74 +1,66 @@
.. _resources:
test resource management and xUnit setup (on steroids)
=======================================================
test resource injection and parametrization
=======================================================
.. _`Dependency injection`: http://en.wikipedia.org/wiki/Dependency_injection
.. versionadded: 2.3
pytest offers advanced resource parametrization and injection mechanisms
including a fully integrated generalization of the popular xUnit
setup-style methods. A resource is created by a ``@pytest.mark.factory``
marked function and its name is the name of the function. A resource
is injected into test or setup functions if they use the name
in their signature. Therefore and also for historic reasons, resources are
sometimes called "funcargs" because they ultimately appear as
function arguments.
The pytest resource management and setup features are exposed through
three decorators:
* a `@pytest.mark.factory`_ marker to define resource factories,
their scoping and parametrization.
* a `@pytest.mark.setup`_ marker to define setup functions and their
scoping.
* a `@pytest.mark.parametrize`_ marker for executing test functions
multiple times with different parameter sets
Generally, resource factories and setup functions:
- can be defined in test modules, test classes, conftest.py files or
in plugins.
- can themselves receive resources through their function arguments,
simplifying the setup and use of interdependent resources.
- can use the special `testcontext`_ object for access to the
context in which the factory/setup is called and for registering
finalizers.
This document showcases these features through some basic examples.
Note that pytest also comes with some :ref:`builtinresources` which
you can use without defining them yourself.
Background and terms
---------------------------
pytest offers very flexible means for managing test resources and
test parametrization.
The pytest resource management mechanism is an example of `Dependency
Injection`_ which helps to de-couple test code from resource
instantiation code required for them to execute. At test writing time
you typically do not need to care for the details of how your required
resources are constructed, if they live through a function, class,
module or session scope or if the test will be called multiple times
with different resource instances.
Injection`_ because test and :ref:`setup functions <xunitsetup>` receive
resources simply by stating them as an input argument. Therefore and
also for historic reasons, they are often called **funcargs**. At test
writing time you typically do not need to care for the details of how
your required resources are constructed, if they live through a
function, class, module or session scope or if the test will be called
multiple times with different resource instances.
To create a value with which to call a test function a resource factory
function is called which gets full access to the test context and can
register finalizers which are to be run after the last test in that context
finished. Resource factories 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.
test module, in a per-directory ``conftest.py`` file or in an external
plugin. This allows **total de-coupling of test and setup code**,
lowering the cost of refactoring.
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.
speak of parametrization. You can parametrize resources or parametrize
test function arguments directly or even implement your own parametrization
scheme through a plugin hook.
A resource has a **name** under which test and setup functions
can access it by listing it as an input argument. Due to this and
also for historic reasons, resources are often called **funcargs**.
A resource is created by a factory which can be flagged with a **scope**
to only create resources on a per-class/per-module/per-session basis
instead of the default per-function scope.
Concretely, there are three means of resource and parametrization management:
* a `@pytest.mark.factory`_ marker to define resource factories,
their scoping and parametrization. Factories can themselves
receive resources through their function arguments, easing
the setup of interdependent resources. They can also use
the special `testcontext`_ object to access details n which
the factory/setup is called and for registering finalizers.
* a `@pytest.mark.parametrize`_ marker for executing test functions
multiple times with different parameter sets
* a `pytest_generate_tests`_ plugin hook marker for implementing
your parametrization for a test function which may depend on
command line options, class/module attributes etc.
Finally, pytest comes with some :ref:`builtinresources` which
you can use without defining them yourself. Moreover, third-party
plugins offer their own resources so that after installation
you can simply use them in your test and setup functions.
.. _`@pytest.mark.factory`:
@ -81,11 +73,12 @@ of test functions.
The `@pytest.mark.factory`_ marker allows to
* mark a function as a factory for resources used by test and setup functions
* define parametrization to run tests multiple times with different
* mark a function as a factory for resources, useable by test and setup functions
* define parameters in order to run tests multiple times with different
resource instances
* set a scope which determines the level of caching. valid scopes
are ``session``, ``module``, ``class`` and ``function``.
* set a scope which determines the level of caching, i.e. how often
the factory will be called. Valid scopes are ``session``, ``module``,
``class`` and ``function``.
Here is a simple example of a factory creating a shared ``smtplib.SMTP``
connection resource which test functions then may use across the whole
@ -248,12 +241,12 @@ Note that pytest orders your test run by resource usage, minimizing
the number of active resources at any given time.
Accessing resources from a factory function
Interdepdendent resources
----------------------------------------------------------
You can directly use resources as funcargs in resource factories.
Extending the previous example we can instantiate an application
object and stick the live ``smtp`` resource into it::
You can not only use resources in test functions but also in resource factories
themselves. Extending the previous example we can instantiate an application
object by sticking the ``smtp`` resource into it::
# content of test_appsetup.py
@ -289,114 +282,6 @@ run twice with two different ``App`` instances and respective smtp servers.
There is no need for the ``app`` factory to be aware of the parametrization.
.. _`new_setup`:
.. _`@pytest.mark.setup`:
``@pytest.mark.setup``: xUnit setup methods on steroids
-----------------------------------------------------------------
.. regendoc:wipe
.. versionadded:: 2.3
The ``@pytest.mark.setup`` marker allows
* to define setup-functions close to test code or in conftest.py files
or plugins.
* to mark a function as a setup method; the function can itself
receive funcargs and will execute multiple times if the funcargs
are parametrized
* to set a scope which influences when the setup function going to be
called. valid scopes are ``session``, ``module``, ``class`` and ``function``.
Here is a simple example. First we define a global ``globdir`` resource::
# content of conftest.py
import pytest
@pytest.mark.factory(scope="module")
def globdir(testcontext, tmpdir):
def fin():
print "finalize", tmpdir
testcontext.addfinalizer(fin)
print "created resource", tmpdir
return tmpdir
And then we write a test file containing a setup-marked function
taking this resource and setting it as a module global::
# content of test_module.py
import pytest
@pytest.mark.setup(scope="module")
def setresource(testcontext, globdir):
print "setupresource", globdir
testcontext.module.myresource = globdir
def test_1():
assert myresource
print "using myresource", myresource
def test_2():
assert myresource
print "using myresource", myresource
Let's run this module::
$ py.test -qs
collecting ... collected 2 items
..
2 passed in 0.26 seconds
created resource /home/hpk/tmp/pytest-4427/test_10
setupresource /home/hpk/tmp/pytest-4427/test_10
using myresource /home/hpk/tmp/pytest-4427/test_10
using myresource /home/hpk/tmp/pytest-4427/test_10
finalize /home/hpk/tmp/pytest-4427/test_10
The two test functions in the module use the same global ``myresource``
object because the ``setresource`` set it as a module attribute.
The ``globdir`` factory can now become parametrized without any test
or setup code needing to change::
# content of conftest.py
import pytest
@pytest.mark.factory(scope="module", params=["aaa", "bbb"])
def globdir(testcontext, tmpdir):
newtmp = tmpdir.join(testcontext.param)
def fin():
print "finalize", newtmp
testcontext.addfinalizer(fin)
print "created resource", newtmp
return newtmp
Running the unchanged previous test files now runs four tests::
$ py.test -qs
collecting ... collected 4 items
....
4 passed in 0.26 seconds
created resource /home/hpk/tmp/pytest-4428/test_1_aaa_0/aaa
setupresource /home/hpk/tmp/pytest-4428/test_1_aaa_0/aaa
using myresource /home/hpk/tmp/pytest-4428/test_1_aaa_0/aaa
using myresource /home/hpk/tmp/pytest-4428/test_1_aaa_0/aaa
finalize /home/hpk/tmp/pytest-4428/test_1_aaa_0/aaa
created resource /home/hpk/tmp/pytest-4428/test_1_bbb_0/bbb
setupresource /home/hpk/tmp/pytest-4428/test_1_bbb_0/bbb
using myresource /home/hpk/tmp/pytest-4428/test_1_bbb_0/bbb
using myresource /home/hpk/tmp/pytest-4428/test_1_bbb_0/bbb
finalize /home/hpk/tmp/pytest-4428/test_1_bbb_0/bbb
Each parameter causes the creation of a respective resource and the
unchanged test module uses it in its ``@setup`` decorated method.
.. note::
Tests using a particular parametrized resource instance will
executed next to each other. Any finalizers will be run before the
next parametrized resource instance is being setup and tests
are rerun.
Grouping tests by resource parameters
----------------------------------------------------------
@ -540,7 +425,10 @@ As expected only one pair of input/output values fails the simple test function.
Note that there are various ways how you can mark groups of functions,
see :ref:`mark`.
Generating parameters combinations, depending on command line
.. _`pytest_generate_tests`:
``pytest_generate_test``: implementing your own parametrization scheme
----------------------------------------------------------------------------
.. regendoc:wipe

204
doc/en/setup.txt Normal file
View File

@ -0,0 +1,204 @@
.. _xunitsetup:
.. _setup:
``@setup`` functions or: xunit on steroids
========================================================
.. versionadded:: 2.3
.. _`funcargs`: funcargs.html
.. _`test parametrization`: funcargs.html#parametrizing-tests
.. _`unittest plugin`: plugin/unittest.html
.. _`xUnit`: http://en.wikipedia.org/wiki/XUnit
Python, Java and many other languages support a so called xUnit_ style
of resource setup. This typically involves the call of a ``setup``
("fixture") method before running a test function and ``teardown`` after
it has finished. Unlike :ref:`injected resources <resources>` setup
functions work indirectly by causing global side effects or
setting test case attributes which test methods can then access.
pytest originally introduced in 2005 a fine-grained model of detecting
setup and teardown functions on a per-module, class or function basis.
The Python unittest module and nose have subsequently incorporated them.
This model remains supported as :ref:`old-style xunit`.
With pytest-2.3 a new ``pytest.setup()`` decorator is introduced
to mark functions as setup functions which:
- can receive resources through funcargs,
- fully interoperate with parametrized resources,
- can be defined in a plugin or conftest.py file and get called
on a per-session, per-module, per-class or per-function basis,
- can access the full :ref:`testcontext` for which the setup is called,
- can precisely control teardown by registering one or multiple
teardown functions as soon as they have performed some actions
which need undoing, eliminating the no need for a separate
"teardown" decorator.
- allow to separate different setup concerns even if they
happen to work in the same scope
All of these features are now demonstrated by little examples.
.. _`new_setup`:
.. _`@pytest.mark.setup`:
basic per-function setup
-------------------------------
.. regendoc:wipe
Suppose you want to have a clean directory with a single
file entry for each test function in a module and have
the test execute with this directory as current working dir::
# content of test_funcdir.py
import pytest
import os
@pytest.mark.setup()
def mydir(tmpdir):
tmpdir.join("myfile").write("example content")
old = tmpdir.chdir()
def test_function1():
assert os.path.exists("myfile")
f = open("anotherfile", "w")
f.write("")
f.close()
def test_function2():
assert os.path.exists("myfile")
assert not os.path.exists("anotherfile")
Our ``mydir`` setup function is executed on a per-function basis,
the default scope used by the ``pytest.mark.setup`` decorator.
It accesses the ``tmpdir`` resource which provides a new empty
directory path object. The ``test_function2`` here checks that
it executes with a fresh directory and specifically
does not see the previously created ``anotherfile``. We can
thus expect two passing tests::
$ py.test -v
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev7 -- /home/hpk/venv/1/bin/python
cachedir: /home/hpk/tmp/doc-exec-410/.cache
plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov
collecting ... collected 2 items
test_funcdir.py:9: test_function1 PASSED
test_funcdir.py:15: test_function2 PASSED
========================= 2 passed in 0.26 seconds =========================
per-function setup, for every function of a project
------------------------------------------------------------
If you want to define a setup per-function but want to apply
it to every function in your project you don't need to duplicate
the setup-definition into each test module. Instead you can put
it into a ``conftest.py`` file into the root of your project::
# content of conftest.py
import pytest
import os
@pytest.mark.setup()
def cleandir(tmpdir):
old = tmpdir.chdir()
The ``cleandir`` setup function will be called for every test function
below the directory tree where ``conftest.py`` resides. In this
case it just uses the builtin ``tmpdir`` resource to change to the
empty directory ahead of running a test.
test modules accessing a global resource
-------------------------------------------------------
If you want test modules to access a global resource,
you can stick the resource to the module globals in
a per-module setup function. We use a :ref:`resource factory
<@pytest.mark.factory>` to create our global resource::
# content of conftest.py
import pytest
class GlobalResource:
def __init__(self):
pass
@pytest.mark.factory(scope="session")
def globresource():
return GlobalResource()
@pytest.mark.setup(scope="module")
def setresource(testcontext, globresource):
testcontext.module.globresource = globresource
Now any test module can access ``globresource`` as a module global::
# content of test_glob.py
def test_1():
print ("test_1 %s" % globresource)
def test_2():
print ("test_2 %s" % globresource)
Let's run this module without output-capturing::
$ py.test -qs test_glob.py
collecting ... collected 2 items
..
2 passed in 0.02 seconds
test_1 <conftest.GlobalResource instance at 0x13197e8>
test_2 <conftest.GlobalResource instance at 0x13197e8>
The two tests see the same global ``globresource`` object.
Parametrizing the global resource
+++++++++++++++++++++++++++++++++++++++++++++++++
We extend the previous example and add parametrization to the globresource
factory and also add a finalizer::
# content of conftest.py
import pytest
class GlobalResource:
def __init__(self, param):
self.param = param
@pytest.mark.factory(scope="session", params=[1,2])
def globresource(testcontext):
g = GlobalResource(testcontext.param)
def fin():
print "finalizing", g
testcontext.addfinalizer(fin)
return g
@pytest.mark.setup(scope="module")
def setresource(testcontext, globresource):
testcontext.module.globresource = globresource
And then re-run our test module::
$ py.test -qs test_glob.py
collecting ... collected 4 items
....
4 passed in 0.02 seconds
test_1 <conftest.GlobalResource instance at 0x1922e18>
test_2 <conftest.GlobalResource instance at 0x1922e18>
finalizing <conftest.GlobalResource instance at 0x1922e18>
test_1 <conftest.GlobalResource instance at 0x1925518>
test_2 <conftest.GlobalResource instance at 0x1925518>
finalizing <conftest.GlobalResource instance at 0x1925518>
We are now running the two tests twice with two different global resource
instances. Note that the tests are ordered such that only
one instance is active at any given time: the finalizer of
the first globresource instance is called before the second
instance is created and sent to the setup functions.

View File

@ -1,23 +1,16 @@
.. _xunitsetup:
====================================
Extended xUnit style setup fixtures
====================================
.. _`old-style xunit`:
.. _`funcargs`: funcargs.html
.. _`test parametrization`: funcargs.html#parametrizing-tests
.. _`unittest plugin`: plugin/unittest.html
.. _`xUnit`: http://en.wikipedia.org/wiki/XUnit
Python, Java and many other languages support xUnit_ style testing.
This typically involves the call of a ``setup`` ("fixture") method
before running a test function and ``teardown`` after it has finished.
``py.test`` supports a more fine-grained model of setup/teardown
handling by optionally calling per-module and per-class hooks.
Old-style xunit-style setup
========================================
This section describes the old way how you can implement setup and
teardown on a per-module/class/function basis. It remains fully
supported but it is recommended to rather use :ref:`@setup functions <setup>`
or :ref:`injected resources <resources>` for implementing your setup needs.
Module level setup/teardown
=============================================
--------------------------------------
If you have multiple test functions and test classes in a single
module you can optionally implement the following fixture methods
@ -32,7 +25,7 @@ which will usually be called once for all the functions::
"""
Class level setup/teardown
=============================================
----------------------------------
Similarly, the following methods are called at class level before
and after all test methods of the class are called::
@ -50,7 +43,7 @@ and after all test methods of the class are called::
"""
Method and function level setup/teardown
=============================================
-----------------------------------------------
Similarly, the following methods are called around each method invocation::