test_ok1/doc/en/setup.txt

213 lines
7.2 KiB
Plaintext
Raw Normal View History

.. _xunitsetup:
.. _setup:
.. _`setup functions`:
.. _`@pytest.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 scope-specific model of detecting
setup and teardown functions on a per-module, class or function basis.
The Python unittest package and nose have subsequently incorporated them.
This model remains supported by pytest as :ref:`old-style xunit`.
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:
- can receive :ref:`resources through funcargs <resources>`,
- fully interoperate with parametrized resources,
- can be defined in a plugin or :ref:`conftest.py <conftest.py>` file and get called
on a per-session, per-module, per-class or per-function basis,
- can access the :ref:`request <request>` 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.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.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.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 that it
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.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
-------------------------------------------------------
.. 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.
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.factory>` to create our global resource::
# content of conftest.py
import pytest
class GlobalResource:
def __init__(self):
pass
@pytest.factory(scope="session")
def globresource():
return GlobalResource()
@pytest.setup(scope="module")
def setresource(request, globresource):
request.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.factory(scope="session", params=[1,2])
def globresource(request):
g = GlobalResource(request.param)
def fin():
print "finalizing", g
request.addfinalizer(fin)
return g
@pytest.setup(scope="module")
def setresource(request, globresource):
request.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.