2012-08-02 18:07:54 +08:00
|
|
|
.. _xunitsetup:
|
|
|
|
.. _setup:
|
2012-08-04 01:08:27 +08:00
|
|
|
.. _`setup functions`:
|
|
|
|
.. _`@pytest.setup`:
|
2012-08-02 18:07:54 +08:00
|
|
|
|
|
|
|
``@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.
|
|
|
|
|
2012-08-04 01:08:27 +08:00
|
|
|
pytest originally introduced in 2005 a scope-specific model of detecting
|
2012-08-02 18:07:54 +08:00
|
|
|
setup and teardown functions on a per-module, class or function basis.
|
2012-08-04 01:08:27 +08:00
|
|
|
The Python unittest package and nose have subsequently incorporated them.
|
|
|
|
This model remains supported by pytest as :ref:`old-style xunit`.
|
2012-08-02 18:07:54 +08:00
|
|
|
|
2012-08-04 01:08:27 +08:00
|
|
|
Moreover, pytest-2.3 introduces a new ``pytest.setup()`` decorator
|
|
|
|
to mark functions as setup functions which allow to implement everything
|
|
|
|
you can do with the old-style and much more. Specifically setup functions:
|
2012-08-02 18:07:54 +08:00
|
|
|
|
2012-08-04 01:08:27 +08:00
|
|
|
- can receive :ref:`resources through funcargs <resources>`,
|
2012-08-02 18:07:54 +08:00
|
|
|
- fully interoperate with parametrized resources,
|
2012-08-04 16:33:43 +08:00
|
|
|
- can be defined in a plugin or :ref:`conftest.py <conftest.py>` file and get called
|
2012-08-02 18:07:54 +08:00
|
|
|
on a per-session, per-module, per-class or per-function basis,
|
2012-09-17 22:36:10 +08:00
|
|
|
- can access the :ref:`request <request>` for which the setup is called,
|
2012-08-02 18:07:54 +08:00
|
|
|
- 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`:
|
2012-08-02 18:41:46 +08:00
|
|
|
.. _`@pytest.setup`:
|
2012-08-02 18:07:54 +08:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2012-08-02 18:41:46 +08:00
|
|
|
@pytest.setup()
|
2012-08-02 18:07:54 +08:00
|
|
|
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,
|
2012-08-02 18:41:46 +08:00
|
|
|
the default scope used by the ``pytest.setup`` decorator.
|
2012-08-02 18:07:54 +08:00
|
|
|
It accesses the ``tmpdir`` resource which provides a new empty
|
|
|
|
directory path object. The ``test_function2`` here checks that
|
2012-08-04 01:08:27 +08:00
|
|
|
it executes with a fresh directory and that it
|
2012-08-02 18:07:54 +08:00
|
|
|
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
|
|
|
|
|
2012-08-02 18:41:46 +08:00
|
|
|
@pytest.setup()
|
2012-08-02 18:07:54 +08:00
|
|
|
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
|
|
|
|
-------------------------------------------------------
|
|
|
|
|
2012-08-04 01:08:27 +08:00
|
|
|
.. note::
|
|
|
|
|
|
|
|
Relying on `global state is considered bad programming practise <http://en.wikipedia.org/wiki/Global_variable>`_ but when you work with an application
|
|
|
|
that relies on it you often have no choice.
|
|
|
|
|
2012-08-02 18:07:54 +08:00
|
|
|
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
|
2012-08-02 18:41:46 +08:00
|
|
|
<@pytest.factory>` to create our global resource::
|
2012-08-02 18:07:54 +08:00
|
|
|
|
|
|
|
# content of conftest.py
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
class GlobalResource:
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
|
2012-08-02 18:41:46 +08:00
|
|
|
@pytest.factory(scope="session")
|
2012-08-02 18:07:54 +08:00
|
|
|
def globresource():
|
|
|
|
return GlobalResource()
|
|
|
|
|
2012-08-02 18:41:46 +08:00
|
|
|
@pytest.setup(scope="module")
|
2012-09-17 22:36:10 +08:00
|
|
|
def setresource(request, globresource):
|
|
|
|
request.module.globresource = globresource
|
2012-08-02 18:07:54 +08:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2012-08-02 18:41:46 +08:00
|
|
|
@pytest.factory(scope="session", params=[1,2])
|
2012-09-17 22:36:10 +08:00
|
|
|
def globresource(request):
|
|
|
|
g = GlobalResource(request.param)
|
2012-08-02 18:07:54 +08:00
|
|
|
def fin():
|
|
|
|
print "finalizing", g
|
2012-09-17 22:36:10 +08:00
|
|
|
request.addfinalizer(fin)
|
2012-08-02 18:07:54 +08:00
|
|
|
return g
|
|
|
|
|
2012-08-02 18:41:46 +08:00
|
|
|
@pytest.setup(scope="module")
|
2012-09-17 22:36:10 +08:00
|
|
|
def setresource(request, globresource):
|
|
|
|
request.module.globresource = globresource
|
2012-08-02 18:07:54 +08:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
|
|
|