merge factories/funcargs and setup functions into the new "fixture" document
This commit is contained in:
parent
3049af618c
commit
439cc1238f
|
@ -269,7 +269,11 @@ epub_copyright = u'2011, holger krekel et alii'
|
|||
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {} # 'http://docs.python.org/': None}
|
||||
intersphinx_mapping = {'python': ('http://docs.python.org/', None),
|
||||
'lib': ("http://docs.python.org/library/", None),
|
||||
}
|
||||
|
||||
|
||||
def setup(app):
|
||||
#from sphinx.ext.autodoc import cut_lines
|
||||
#app.connect('autodoc-process-docstring', cut_lines(4, what=['module']))
|
||||
|
|
|
@ -0,0 +1,681 @@
|
|||
.. _xunitsetup:
|
||||
.. _setup:
|
||||
.. _fixture:
|
||||
.. _`fixture functions`:
|
||||
.. _`@pytest.fixture`:
|
||||
|
||||
pytest fixtures: modular, re-useable, flexible
|
||||
========================================================
|
||||
|
||||
.. versionadded:: 2.0,2.3
|
||||
|
||||
.. _`funcargs`: funcargs.html
|
||||
.. _`test parametrization`: funcargs.html#parametrizing-tests
|
||||
.. _`unittest plugin`: plugin/unittest.html
|
||||
.. _`xUnit`: http://en.wikipedia.org/wiki/XUnit
|
||||
.. _`general purpose of test fixtures`: http://en.wikipedia.org/wiki/Test_fixture#Software
|
||||
.. _`django`: https://www.djangoproject.com/
|
||||
.. _`pytest-django`: https://pypi.python.org/pytest-django
|
||||
.. _`Dependency injection`: http://en.wikipedia.org/wiki/Dependency_injection#Definition
|
||||
|
||||
pytest allows to provide and use test fixtures in a modular and flexible
|
||||
manner, offering major improvements over the classic xUnit style of
|
||||
setup/teardown functions. The `general purpose of test fixtures`_ is to
|
||||
provide a fixed baseline upon which tests can reliably and
|
||||
repeatedly execute. With pytest, fixtures are implemented by
|
||||
**fixture functions** which may return a fixture object, put extra
|
||||
attributes on test classes or perform side effects. The name of a
|
||||
fixture function is significant and is used for invoking or activating it.
|
||||
|
||||
**Test functions can receive fixture objects by naming them as an input
|
||||
argument.** For each argument name, a matching fixture
|
||||
function will provide a fixture object. This mechanism has been
|
||||
introduced with pytest-2.0 and is also called the **funcarg mechanism**.
|
||||
It allows test functions to easily receive and work against specific
|
||||
pre-initialized application objects without having to care about the
|
||||
details of setup/cleanup procedures. This mechanism is a prime example of
|
||||
`dependency injection`_ where fixture functions take the role of the
|
||||
*injector* and test functions are the *consumers* of fixture objects.
|
||||
With pytest-2.3 this mechanism has been much improved to help with
|
||||
sharing and parametrizing fixtures across test runs.
|
||||
|
||||
**Test classes, modules or whole projects can declare a need for
|
||||
one or more fixtures**. All required fixture functions will execute
|
||||
before a test from the specifying context executes. They will
|
||||
typically not provide a fixture object but rather perform side effects
|
||||
like reading or preparing default config settings and pre-initializing
|
||||
an application. For example, the Django_ project requires database
|
||||
initialization to be able to import from and use its model objects.
|
||||
Plugins like `pytest-django`_ provide baseline fixtures which your
|
||||
project can then easily depend or extend on.
|
||||
|
||||
**Fixtures can be shared throughout a test session, module or class.**.
|
||||
By means of a "scope" declaration on a fixture function, it will
|
||||
only be invoked once per the specified scope. Sharing expensive application
|
||||
object setups between tests typically helps to speed up test runs.
|
||||
Typical examples are the setup of test databases or establishing
|
||||
required subprocesses or network connections.
|
||||
|
||||
**Fixture functions have controlled visilibity** which depends on where they
|
||||
are defined. If they are defined on a test class, only its test methods
|
||||
may use it. A fixture defined in a module can only be used
|
||||
from that test module. A fixture defined in a conftest.py file
|
||||
can only be used by the tests below the directory of that file.
|
||||
Lastly plugins can define fixtures which are available across all
|
||||
projects.
|
||||
|
||||
**Fixture functions can interact with the requesting testcontext**. By
|
||||
accepting a special ``request`` object, fixture functions can introspect
|
||||
the function, class or module for which they are invoked and can
|
||||
optionally register cleanup functions which are called when the last
|
||||
test finished execution. A good example is `pytest-timeout`_ which
|
||||
allows to limit the execution time of a test, and will read the
|
||||
according parameter from a test function or from project-wide setting.
|
||||
|
||||
**Fixture functions can be parametrized** in which case they will be called
|
||||
multiple times, each time executing the set of dependent tests, i. e. the
|
||||
tests that depend on this fixture. Test functions do usually not need
|
||||
to be aware of their re-running. Fixture parametrization helps to
|
||||
write functional tests for components which themselves can be
|
||||
configured in multiple ways.
|
||||
|
||||
|
||||
Basic funcarg fixture example
|
||||
-----------------------------------------------------------
|
||||
|
||||
.. versionadded:: 2.3
|
||||
|
||||
|
||||
Let's look at a simple self-contained test module containing a module
|
||||
visible fixture function and a test function using the provided fixture::
|
||||
|
||||
# content of ./test_simplefactory.py
|
||||
import pytest
|
||||
|
||||
@pytest.fixture
|
||||
def myfuncarg():
|
||||
return 42
|
||||
|
||||
def test_function(myfuncarg):
|
||||
assert myfuncarg == 17
|
||||
|
||||
Here, the ``test_function`` needs an object named ``myfuncarg`` and thus
|
||||
py.test will discover and call the ``@pytest.fixture`` marked ``myfuncarg``
|
||||
factory function. Running the tests looks like this::
|
||||
|
||||
$ py.test test_simplefactory.py
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11
|
||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
||||
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:8: AssertionError
|
||||
========================= 1 failed in 0.01 seconds =========================
|
||||
|
||||
This shows that the test function was called with a ``myfuncarg``
|
||||
argument value of ``42`` and the assert fails as expected. Here is
|
||||
how py.test comes to call the test function this way:
|
||||
|
||||
1. py.test :ref:`finds <test discovery>` 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 a fixture function named ``myfuncarg``.
|
||||
|
||||
2. ``myfuncarg()`` is called to create a value ``42``.
|
||||
|
||||
3. ``test_function(42)`` is now called and results in the above
|
||||
reported 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.
|
||||
|
||||
.. Note::
|
||||
|
||||
You can always issue::
|
||||
|
||||
py.test --fixtures test_simplefactory.py
|
||||
|
||||
to see available fixtures.
|
||||
|
||||
In versions prior to 2.3 there was no @pytest.fixture marker
|
||||
and you had to instead use a magic ``pytest_funcarg__NAME`` prefix
|
||||
for the fixture factory. This remains and will remain supported
|
||||
but is not advertised as the primary means of declaring fixture
|
||||
functions.
|
||||
|
||||
|
||||
Creating and using a session-shared fixture
|
||||
-----------------------------------------------------------------
|
||||
|
||||
.. regendoc:wipe
|
||||
|
||||
Here is a simple example of a fixture function creating a shared
|
||||
``smtplib.SMTP`` connection fixture which test functions from
|
||||
test modules below the directory of a ``conftest.py`` file may use::
|
||||
|
||||
# content of conftest.py
|
||||
import pytest
|
||||
import smtplib
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def smtp():
|
||||
return smtplib.SMTP("merlinux.eu")
|
||||
|
||||
The name of the fixture is ``smtp`` and you can access its result by
|
||||
listing the name ``smtp`` as an input parameter in any test or setup
|
||||
function::
|
||||
|
||||
# content of test_module.py
|
||||
def test_ehlo(smtp):
|
||||
response = smtp.ehlo()
|
||||
assert response[0] == 250
|
||||
assert "merlinux" in response[1]
|
||||
assert 0 # for demo purposes
|
||||
|
||||
def test_noop(smtp):
|
||||
response = smtp.noop()
|
||||
assert response[0] == 250
|
||||
assert 0 # for demo purposes
|
||||
|
||||
We deliberately insert failing ``assert 0`` statements in order to
|
||||
inspect what is going on and can now run the tests::
|
||||
|
||||
$ py.test -q test_module.py
|
||||
collecting ... collected 2 items
|
||||
FF
|
||||
================================= FAILURES =================================
|
||||
________________________________ test_ehlo _________________________________
|
||||
|
||||
smtp = <smtplib.SMTP instance at 0x31bce18>
|
||||
|
||||
def test_ehlo(smtp):
|
||||
response = smtp.ehlo()
|
||||
assert response[0] == 250
|
||||
assert "merlinux" in response[1]
|
||||
> assert 0 # for demo purposes
|
||||
E assert 0
|
||||
|
||||
test_module.py:5: AssertionError
|
||||
________________________________ test_noop _________________________________
|
||||
|
||||
smtp = <smtplib.SMTP instance at 0x31bce18>
|
||||
|
||||
def test_noop(smtp):
|
||||
response = smtp.noop()
|
||||
assert response[0] == 250
|
||||
> assert 0 # for demo purposes
|
||||
E assert 0
|
||||
|
||||
test_module.py:10: AssertionError
|
||||
2 failed in 0.26 seconds
|
||||
|
||||
you see the two ``assert 0`` failing and can also see that
|
||||
the same (session-scoped) object was passed into the two test functions
|
||||
because pytest shows the incoming arguments in the traceback.
|
||||
|
||||
Adding a finalizer to a fixture
|
||||
--------------------------------------------------------
|
||||
|
||||
Further extending the ``smtp`` example, we now want to properly
|
||||
close a smtp server connection after the last test using it
|
||||
has been run. We can do this by calling the ``request.addfinalizer()``
|
||||
helper::
|
||||
|
||||
# content of conftest.py
|
||||
import pytest
|
||||
import smtplib
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def smtp(request):
|
||||
smtp = smtplib.SMTP("merlinux.eu")
|
||||
def fin():
|
||||
print ("finalizing %s" % smtp)
|
||||
smtp.close()
|
||||
request.addfinalizer(fin)
|
||||
return smtp
|
||||
|
||||
The registered ``fin`` function will be called when the last test
|
||||
using it has executed::
|
||||
|
||||
$ py.test -s -q --tb=no
|
||||
collecting ... collected 4 items
|
||||
FFFF
|
||||
4 failed in 6.40 seconds
|
||||
finalizing <smtplib.SMTP instance at 0x125d3b0>
|
||||
|
||||
We see that the ``smtp`` instance is finalized after all
|
||||
tests executed. If we had specified ``scope='function'``
|
||||
then fixture setup and cleanup would occur around each
|
||||
single test.
|
||||
|
||||
Parametrizing a session-shared funcarg resource
|
||||
-----------------------------------------------------------------
|
||||
|
||||
Extending the previous example, we can flag the fixture to create
|
||||
two ``smtp`` fixture instances which will cause all tests using the
|
||||
fixture to run twice. The fixture function gets
|
||||
access to each parameter through the special `request`_ object::
|
||||
|
||||
# content of conftest.py
|
||||
import pytest
|
||||
import smtplib
|
||||
|
||||
@pytest.fixture(scope="session",
|
||||
params=["merlinux.eu", "mail.python.org"])
|
||||
def smtp(request):
|
||||
smtp = smtplib.SMTP(request.param)
|
||||
def fin():
|
||||
print ("finalizing %s" % smtp)
|
||||
smtp.close()
|
||||
request.addfinalizer(fin)
|
||||
return smtp
|
||||
|
||||
The main change is the declaration of ``params``, a list of values
|
||||
for each of which the fixture function will execute and can access
|
||||
a value via ``request.param``. No test function code needs to change.
|
||||
So let's just do another run::
|
||||
|
||||
$ py.test -q
|
||||
collecting ... collected 4 items
|
||||
FFFF
|
||||
================================= FAILURES =================================
|
||||
__________________________ test_ehlo[merlinux.eu] __________________________
|
||||
|
||||
smtp = <smtplib.SMTP instance at 0x28dc5a8>
|
||||
|
||||
def test_ehlo(smtp):
|
||||
response = smtp.ehlo()
|
||||
assert response[0] == 250
|
||||
assert "merlinux" in response[1]
|
||||
> assert 0 # for demo purposes
|
||||
E assert 0
|
||||
|
||||
test_module.py:5: AssertionError
|
||||
__________________________ test_noop[merlinux.eu] __________________________
|
||||
|
||||
smtp = <smtplib.SMTP instance at 0x28dc5a8>
|
||||
|
||||
def test_noop(smtp):
|
||||
response = smtp.noop()
|
||||
assert response[0] == 250
|
||||
> assert 0 # for demo purposes
|
||||
E assert 0
|
||||
|
||||
test_module.py:10: AssertionError
|
||||
________________________ test_ehlo[mail.python.org] ________________________
|
||||
|
||||
smtp = <smtplib.SMTP instance at 0x28e3e18>
|
||||
|
||||
def test_ehlo(smtp):
|
||||
response = smtp.ehlo()
|
||||
assert response[0] == 250
|
||||
> assert "merlinux" in response[1]
|
||||
E assert 'merlinux' in 'mail.python.org\nSIZE 10240000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN'
|
||||
|
||||
test_module.py:4: AssertionError
|
||||
________________________ test_noop[mail.python.org] ________________________
|
||||
|
||||
smtp = <smtplib.SMTP instance at 0x28e3e18>
|
||||
|
||||
def test_noop(smtp):
|
||||
response = smtp.noop()
|
||||
assert response[0] == 250
|
||||
> assert 0 # for demo purposes
|
||||
E assert 0
|
||||
|
||||
test_module.py:10: AssertionError
|
||||
4 failed in 6.17 seconds
|
||||
|
||||
We now get four failures because we are running the two tests twice with
|
||||
different ``smtp`` fixture instances. Note that with the
|
||||
``mail.python.org`` connection the second test fails in ``test_ehlo``
|
||||
because it expects a specific server string.
|
||||
|
||||
We also see that the two ``smtp`` instances are finalized appropriately.
|
||||
|
||||
Looking at test collection without running tests
|
||||
------------------------------------------------------
|
||||
|
||||
You can also look at the tests which pytest collects without running them::
|
||||
|
||||
$ py.test --collectonly
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11
|
||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
||||
collecting ... collected 4 items
|
||||
<Module 'test_module.py'>
|
||||
<Function 'test_ehlo[merlinux.eu]'>
|
||||
<Function 'test_noop[merlinux.eu]'>
|
||||
<Function 'test_ehlo[mail.python.org]'>
|
||||
<Function 'test_noop[mail.python.org]'>
|
||||
|
||||
============================= in 0.01 seconds =============================
|
||||
|
||||
Our fixture parameters show up in the test id of the test functions.
|
||||
Note that pytest orders your test run by resource usage, minimizing
|
||||
the number of active resources at any given time.
|
||||
|
||||
|
||||
.. _`interdependent fixtures`:
|
||||
|
||||
Interdepdendent fixtures
|
||||
----------------------------------------------------------
|
||||
|
||||
You can not only use fixtures in test functions but fixture functions
|
||||
can use other fixtures themselves. This contributes to a modular design
|
||||
of your fixtures and allows re-use of framework-specific fixtures across
|
||||
many projects. As a simple example, we can extend the previous example
|
||||
and instantiate an object ``app`` where we stick the already defined
|
||||
``smtp`` resource into it::
|
||||
|
||||
# content of test_appsetup.py
|
||||
|
||||
import pytest
|
||||
|
||||
class App:
|
||||
def __init__(self, smtp):
|
||||
self.smtp = smtp
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def app(smtp):
|
||||
return App(smtp)
|
||||
|
||||
def test_smtp_exists(app):
|
||||
assert app.smtp
|
||||
|
||||
Here we declare an ``app`` fixture which receives the previously defined
|
||||
``smtp`` fixture and instantiates an ``App`` object with it. Let's run it::
|
||||
|
||||
$ py.test -v test_appsetup.py
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 -- /home/hpk/venv/1/bin/python
|
||||
cachedir: /home/hpk/tmp/doc-exec-423/.cache
|
||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
||||
collecting ... collected 2 items
|
||||
|
||||
test_appsetup.py:12: test_exists[merlinux.eu] PASSED
|
||||
test_appsetup.py:12: test_exists[mail.python.org] PASSED
|
||||
|
||||
========================= 2 passed in 6.82 seconds =========================
|
||||
|
||||
Due to the parametrization of ``smtp`` the test will run twice with two
|
||||
different ``App`` instances and respective smtp servers. There is no
|
||||
need for the ``app`` fixture to be aware of the ``smtp`` parametrization
|
||||
as pytest will fully analyse the fixture dependency graph. Note also,
|
||||
that the ``app`` fixture has a scope of ``module`` but uses a
|
||||
session-scoped ``smtp``: it is fine for fixtures to use "broader" scoped
|
||||
fixtures but not the other way round: A session-scoped fixture could
|
||||
not use a module-scoped one in a meaningful way.
|
||||
|
||||
.. _`automatic per-resource grouping`:
|
||||
|
||||
Automatic grouping of tests by fixture instances
|
||||
----------------------------------------------------------
|
||||
|
||||
.. regendoc: wipe
|
||||
|
||||
pytest minimizes the number of active fixtures during test runs.
|
||||
If you have a parametrized fixture, then all the tests using it will
|
||||
first execute with one instance and then finalizers are called
|
||||
before the next fixture instance is created. Among other things,
|
||||
this eases testing of applications which create and use global state.
|
||||
|
||||
The following example uses two parametrized funcargs, one of which is
|
||||
scoped on a per-module basis, and all the functions perform ``print`` call s
|
||||
to show the flow of calls::
|
||||
|
||||
# content of test_module.py
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope="module", params=["mod1", "mod2"])
|
||||
def modarg(request):
|
||||
param = request.param
|
||||
print "create", param
|
||||
def fin():
|
||||
print "fin", param
|
||||
request.addfinalizer(fin)
|
||||
return param
|
||||
|
||||
@pytest.fixture(scope="function", params=[1,2])
|
||||
def otherarg(request):
|
||||
return request.param
|
||||
|
||||
def test_0(otherarg):
|
||||
print " test0", otherarg
|
||||
def test_1(modarg):
|
||||
print " test1", modarg
|
||||
def test_2(otherarg, modarg):
|
||||
print " test2", otherarg, modarg
|
||||
|
||||
Let's run the tests in verbose mode and with looking at the print-output::
|
||||
|
||||
$ py.test -v -s test_module.py
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 -- /home/hpk/venv/1/bin/python
|
||||
cachedir: /home/hpk/tmp/doc-exec-423/.cache
|
||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
||||
collecting ... collected 8 items
|
||||
|
||||
test_module.py:16: test_0[1] PASSED
|
||||
test_module.py:16: test_0[2] PASSED
|
||||
test_module.py:18: test_1[mod1] PASSED
|
||||
test_module.py:20: test_2[1-mod1] PASSED
|
||||
test_module.py:20: test_2[2-mod1] PASSED
|
||||
test_module.py:18: test_1[mod2] PASSED
|
||||
test_module.py:20: test_2[1-mod2] PASSED
|
||||
test_module.py:20: test_2[2-mod2] PASSED
|
||||
|
||||
========================= 8 passed in 0.02 seconds =========================
|
||||
test0 1
|
||||
test0 2
|
||||
create mod1
|
||||
test1 mod1
|
||||
test2 1 mod1
|
||||
test2 2 mod1
|
||||
fin mod1
|
||||
create mod2
|
||||
test1 mod2
|
||||
test2 1 mod2
|
||||
test2 2 mod2
|
||||
fin mod2
|
||||
|
||||
You can see that the parametrized module-scoped ``modarg`` resource caused
|
||||
an ordering of test execution that lead to the fewest possible "active" resources. The finalizer for the ``mod1`` parametrized resource was executed
|
||||
before the ``mod2`` resource was setup.
|
||||
|
||||
|
||||
Marking test classes, modules, projects with required fixtures
|
||||
----------------------------------------------------------------------
|
||||
|
||||
.. regendoc:wipe
|
||||
|
||||
Sometimes test functions do not directly get access to a fixture object.
|
||||
For example, each test in a test class may require to operate with an
|
||||
empty directory as the current working directory. Here is how you can
|
||||
can use the standard :ref:`tempfile <lib:tempfile>` and pytest fixtures
|
||||
to achieve it. We separate the creation of the fixture into
|
||||
a conftest.py file::
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
import pytest
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
@pytest.fixture()
|
||||
def cleandir():
|
||||
newpath = tempfile.mkdtemp()
|
||||
os.chdir(newpath)
|
||||
|
||||
and declare its use in a test module via a ``needs`` marker::
|
||||
|
||||
# content of test_setenv.py
|
||||
import os
|
||||
import pytest
|
||||
|
||||
@pytest.mark.needsfixtures("cleandir")
|
||||
class TestDirectoryInit:
|
||||
def test_cwd_starts_empty(self):
|
||||
assert os.listdir(os.getcwd()) == []
|
||||
with open("myfile", "w") as f:
|
||||
f.write("hello")
|
||||
|
||||
def test_cwd_again_starts_empty(self):
|
||||
assert os.listdir(os.getcwd()) == []
|
||||
|
||||
Due to the ``needs`` class marker, the ``cleandir`` fixture
|
||||
will be required for the execution of each of the test methods, just as if
|
||||
you specified a "cleandir" function argument to each of them. Let's run it
|
||||
to verify our fixture is activated::
|
||||
|
||||
$ py.test -q
|
||||
collecting ... collected 2 items
|
||||
.
|
||||
2 passed in 0.01 seconds
|
||||
|
||||
You may specify the need for multiple fixtures::
|
||||
|
||||
@pytest.mark.needsfixtures("cleandir", "anotherfixture")
|
||||
|
||||
and you may specify fixture needs at the test module level, using
|
||||
a generic feature of the mark mechanism::
|
||||
|
||||
pytestmark = pytest.mark.needsfixtures("cleandir")
|
||||
|
||||
Lastly you can put fixtures required by all tests in your project
|
||||
into an ini-file::
|
||||
|
||||
# content of pytest.ini
|
||||
|
||||
[pytest]
|
||||
needsfixtures = cleandir
|
||||
|
||||
Implicit fixtures at class/module/directory/global level
|
||||
----------------------------------------------------------------------
|
||||
|
||||
.. regendoc:wipe
|
||||
|
||||
Occasionally, you may want to have fixtures get invoked automatically
|
||||
without any ``needs`` reference. Also, if you are used to the classical
|
||||
xUnit setup/teardown functions you may have gotten used to fixture
|
||||
functions executing always. As a practical example,
|
||||
suppose we have a database fixture which has a begin/rollback/commit
|
||||
architecture and we want to surround each test method by a transaction
|
||||
and a rollback. Here is a dummy self-contained implementation::
|
||||
|
||||
# content of test_db_transact.py
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
class db:
|
||||
def __init__(self):
|
||||
self.intransaction = False
|
||||
def begin(self):
|
||||
self.intransaction = True
|
||||
def rollback(Self):
|
||||
self.intransaction = False
|
||||
|
||||
class TestClass:
|
||||
@pytest.fixture(auto=True)
|
||||
def transact(self, request, db):
|
||||
db.begin()
|
||||
request.addfinalizer(db.rollback)
|
||||
|
||||
def test_method1(self, db):
|
||||
assert db.intransaction
|
||||
|
||||
def test_method2(self):
|
||||
pass
|
||||
|
||||
The class-level ``transact`` fixture is marked with *auto=true* which will
|
||||
mark all test methods in the class as needing the fixture.
|
||||
|
||||
Here is how this maps to module, project and cross-project scopes:
|
||||
|
||||
- if an automatic fixture was defined in a test module, all its test
|
||||
functions would automatically invoke it.
|
||||
|
||||
- if defined in a conftest.py file then all tests in all test
|
||||
modules belows its directory will invoke the fixture.
|
||||
|
||||
- lastly, and **please use that with care**: if you define an automatic
|
||||
fixture in a plugin, it will be invoked for all tests in all projects
|
||||
where the plugin is installed. This can be useful if a fixture only
|
||||
anyway works in the presence of certain settings in the ini-file. Such
|
||||
a global fixture should thus quickly determine if it should do
|
||||
any work and avoid expensive imports or computation otherwise.
|
||||
|
||||
Note that the above ``transact`` fixture may very well be something that
|
||||
you want to make available in your project without having each test function
|
||||
in your project automatically using it. The canonical way to do that is to put
|
||||
the transact definition into a conftest.py file without using ``auto``::
|
||||
|
||||
# content of conftest.py
|
||||
@pytest.fixture()
|
||||
def transact(self, request, db):
|
||||
db.begin()
|
||||
request.addfinalizer(db.rollback)
|
||||
|
||||
and then have a TestClass using it by declaring the need::
|
||||
|
||||
@pytest.mark.needsfixtures("transact")
|
||||
class TestClass:
|
||||
def test_method1(self):
|
||||
...
|
||||
|
||||
While all test methods in this TestClass will thus use the transaction
|
||||
fixture, other test classes will not unless they state the need.
|
||||
|
||||
.. currentmodule:: _pytest.python
|
||||
|
||||
.. _`@pytest.fixture`:
|
||||
|
||||
``@pytest.fixture``: marking a fixture function
|
||||
--------------------------------------------------------------
|
||||
|
||||
The ``@pytest.fixture`` marker allows to
|
||||
|
||||
* mark a function as a factory for fixtures, useable by test and other
|
||||
fixture functions
|
||||
|
||||
* declare 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``.
|
||||
|
||||
* define a list of parameters in order to run dependent tests multiple
|
||||
times with different fixtures
|
||||
|
||||
.. _`request`:
|
||||
|
||||
``request``: interacting with test invocation context
|
||||
--------------------------------------------------------------
|
||||
|
||||
The ``request`` object may be received by fixture functions
|
||||
and provides methods to:
|
||||
|
||||
* to inspect attributes of the requesting test context, such as
|
||||
``function``, ``cls``, ``module``, ``session`` and the pytest
|
||||
``config`` object. A request object passed to a parametrized factory
|
||||
will also carry a ``request.param`` object (A parametrized factory and
|
||||
all of its dependent tests will be called with each of the factory-specified
|
||||
``params``).
|
||||
|
||||
* to add finalizers/teardowns to be invoked when the last
|
||||
test of the requesting test context executes
|
||||
|
||||
.. autoclass:: _pytest.python.FuncargRequest()
|
||||
:members:
|
||||
|
|
@ -159,366 +159,6 @@ to a :ref:`conftest.py <conftest.py>` file or even separately installable
|
|||
funcarg factories starts at test classes, then test modules, then
|
||||
``conftest.py`` files and finally builtin and 3-rd party plugins.
|
||||
|
||||
Creating and using a session-shared funcarg
|
||||
-----------------------------------------------------------------
|
||||
|
||||
.. regendoc:wipe
|
||||
|
||||
.. versionadded:: 2.3
|
||||
|
||||
The `@pytest.factory`_ marker allows to
|
||||
|
||||
* 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
|
||||
|
||||
* declare 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
|
||||
test session::
|
||||
|
||||
# content of conftest.py
|
||||
import pytest
|
||||
import smtplib
|
||||
|
||||
@pytest.factory(scope="session")
|
||||
def smtp(request):
|
||||
return smtplib.SMTP("merlinux.eu")
|
||||
|
||||
The name of the factory is ``smtp`` (the factory function name)
|
||||
and you can access its result by listing the name ``smtp`` as
|
||||
an input parameter in any test or setup function::
|
||||
|
||||
# content of test_module.py
|
||||
def test_ehlo(smtp):
|
||||
response = smtp.ehlo()
|
||||
assert response[0] == 250
|
||||
assert "merlinux" in response[1]
|
||||
assert 0 # for demo purposes
|
||||
|
||||
def test_noop(smtp):
|
||||
response = smtp.noop()
|
||||
assert response[0] == 250
|
||||
assert 0 # for demo purposes
|
||||
|
||||
We deliberately insert failing ``assert 0`` statements in order to
|
||||
inspect what is going on and can now run the tests::
|
||||
|
||||
$ py.test -q test_module.py
|
||||
collecting ... collected 2 items
|
||||
FF
|
||||
================================= FAILURES =================================
|
||||
________________________________ test_ehlo _________________________________
|
||||
|
||||
smtp = <smtplib.SMTP instance at 0x31bce18>
|
||||
|
||||
def test_ehlo(smtp):
|
||||
response = smtp.ehlo()
|
||||
assert response[0] == 250
|
||||
assert "merlinux" in response[1]
|
||||
> assert 0 # for demo purposes
|
||||
E assert 0
|
||||
|
||||
test_module.py:5: AssertionError
|
||||
________________________________ test_noop _________________________________
|
||||
|
||||
smtp = <smtplib.SMTP instance at 0x31bce18>
|
||||
|
||||
def test_noop(smtp):
|
||||
response = smtp.noop()
|
||||
assert response[0] == 250
|
||||
> assert 0 # for demo purposes
|
||||
E assert 0
|
||||
|
||||
test_module.py:10: AssertionError
|
||||
2 failed in 0.26 seconds
|
||||
|
||||
you see the two ``assert 0`` failing and can also see that
|
||||
the same (session-scoped) object was passed into the two test functions.
|
||||
|
||||
|
||||
Parametrizing a session-shared funcarg resource
|
||||
-----------------------------------------------------------------
|
||||
|
||||
Extending the previous example, we can flag the factory to create
|
||||
two ``smtp`` values which will cause all tests using it to
|
||||
run twice with two different values. The factory function gets
|
||||
access to each parameter through the special `request`_ object::
|
||||
|
||||
# content of conftest.py
|
||||
import pytest
|
||||
import smtplib
|
||||
|
||||
@pytest.factory(scope="session",
|
||||
params=["merlinux.eu", "mail.python.org"])
|
||||
def smtp(request):
|
||||
return smtplib.SMTP(request.param)
|
||||
|
||||
The main change is the definition of a ``params`` list in the
|
||||
``factory``-marker and the ``request.param`` access within the
|
||||
factory function. No test function code needs to change.
|
||||
So let's just do another run::
|
||||
|
||||
$ py.test -q
|
||||
collecting ... collected 4 items
|
||||
FFFF
|
||||
================================= FAILURES =================================
|
||||
__________________________ test_ehlo[merlinux.eu] __________________________
|
||||
|
||||
smtp = <smtplib.SMTP instance at 0x28dc5a8>
|
||||
|
||||
def test_ehlo(smtp):
|
||||
response = smtp.ehlo()
|
||||
assert response[0] == 250
|
||||
assert "merlinux" in response[1]
|
||||
> assert 0 # for demo purposes
|
||||
E assert 0
|
||||
|
||||
test_module.py:5: AssertionError
|
||||
__________________________ test_noop[merlinux.eu] __________________________
|
||||
|
||||
smtp = <smtplib.SMTP instance at 0x28dc5a8>
|
||||
|
||||
def test_noop(smtp):
|
||||
response = smtp.noop()
|
||||
assert response[0] == 250
|
||||
> assert 0 # for demo purposes
|
||||
E assert 0
|
||||
|
||||
test_module.py:10: AssertionError
|
||||
________________________ test_ehlo[mail.python.org] ________________________
|
||||
|
||||
smtp = <smtplib.SMTP instance at 0x28e3e18>
|
||||
|
||||
def test_ehlo(smtp):
|
||||
response = smtp.ehlo()
|
||||
assert response[0] == 250
|
||||
> assert "merlinux" in response[1]
|
||||
E assert 'merlinux' in 'mail.python.org\nSIZE 10240000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN'
|
||||
|
||||
test_module.py:4: AssertionError
|
||||
________________________ test_noop[mail.python.org] ________________________
|
||||
|
||||
smtp = <smtplib.SMTP instance at 0x28e3e18>
|
||||
|
||||
def test_noop(smtp):
|
||||
response = smtp.noop()
|
||||
assert response[0] == 250
|
||||
> assert 0 # for demo purposes
|
||||
E assert 0
|
||||
|
||||
test_module.py:10: AssertionError
|
||||
4 failed in 6.17 seconds
|
||||
|
||||
We get four failures because we are running the two tests twice with
|
||||
different ``smtp`` instantiations as defined on the factory.
|
||||
Note that with the ``mail.python.org`` connection the second test
|
||||
fails in ``test_ehlo`` because it expects a specific server string.
|
||||
|
||||
Adding a finalizer to the parametrized resource
|
||||
--------------------------------------------------------
|
||||
|
||||
Further extending the ``smtp`` example, we now want to properly
|
||||
close a smtp server connection after the last test using it
|
||||
has been run. We can do this by calling the ``request.addfinalizer()``
|
||||
helper::
|
||||
|
||||
# content of conftest.py
|
||||
import pytest
|
||||
import smtplib
|
||||
|
||||
@pytest.factory(scope="session",
|
||||
params=["merlinux.eu", "mail.python.org"])
|
||||
def smtp(request):
|
||||
smtp = smtplib.SMTP(request.param)
|
||||
def fin():
|
||||
print ("finalizing %s" % smtp)
|
||||
smtp.close()
|
||||
request.addfinalizer(fin)
|
||||
return smtp
|
||||
|
||||
We also add a print call and then run py.test without the default
|
||||
output capturing and disabled traceback reporting::
|
||||
|
||||
$ py.test -s -q --tb=no
|
||||
collecting ... collected 4 items
|
||||
FFFF
|
||||
4 failed in 6.40 seconds
|
||||
finalizing <smtplib.SMTP instance at 0x125d3b0>
|
||||
finalizing <smtplib.SMTP instance at 0x1265b90>
|
||||
|
||||
We see that the two ``smtp`` instances are finalized appropriately.
|
||||
|
||||
Looking at test collection without running tests
|
||||
------------------------------------------------------
|
||||
|
||||
You can also look at what tests pytest collects without running them::
|
||||
|
||||
$ py.test --collectonly
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11
|
||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
||||
collecting ... collected 4 items
|
||||
<Module 'test_module.py'>
|
||||
<Function 'test_ehlo[merlinux.eu]'>
|
||||
<Function 'test_noop[merlinux.eu]'>
|
||||
<Function 'test_ehlo[mail.python.org]'>
|
||||
<Function 'test_noop[mail.python.org]'>
|
||||
|
||||
============================= in 0.01 seconds =============================
|
||||
|
||||
Note that pytest orders your test run by resource usage, minimizing
|
||||
the number of active resources at any given time.
|
||||
|
||||
|
||||
.. _`interdependent resources`:
|
||||
|
||||
Interdepdendent resources
|
||||
----------------------------------------------------------
|
||||
|
||||
You can not only use funcargs in test functions but also in their factories
|
||||
themselves. Extending the previous example we can instantiate another
|
||||
object ``app`` and stick the ``smtp`` resource into it like this::
|
||||
|
||||
# content of test_appsetup.py
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.factory(scope="module")
|
||||
class app:
|
||||
def __init__(self, smtp):
|
||||
self.smtp = smtp
|
||||
|
||||
def test_exists(app):
|
||||
assert app.smtp
|
||||
|
||||
Here we declare an ``app`` class to be a factory and have its init-method
|
||||
use the previously defined ``smtp`` resource. Let's run it::
|
||||
|
||||
$ py.test -v test_appsetup.py
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 -- /home/hpk/venv/1/bin/python
|
||||
cachedir: /home/hpk/tmp/doc-exec-423/.cache
|
||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
||||
collecting ... collected 2 items
|
||||
|
||||
test_appsetup.py:12: test_exists[merlinux.eu] PASSED
|
||||
test_appsetup.py:12: test_exists[mail.python.org] PASSED
|
||||
|
||||
========================= 2 passed in 6.82 seconds =========================
|
||||
|
||||
Due to the parametrization of ``smtp`` the test will 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. Note
|
||||
that the ``app`` factory has a scope of ``module`` but uses the
|
||||
session-scoped ``smtp`` object: it is fine for factories to use
|
||||
"broader" scoped resources but not the other way round. A
|
||||
session-scoped resource could not use a module-scoped resource in a
|
||||
meaningful way.
|
||||
|
||||
.. _`automatic per-resource grouping`:
|
||||
|
||||
Grouping tests by resource parameters
|
||||
----------------------------------------------------------
|
||||
|
||||
.. regendoc: wipe
|
||||
|
||||
pytest minimizes the number of active resources during test runs.
|
||||
If you have a parametrized resource, then all the tests using one
|
||||
resource instance will execute one after another. Then any finalizers
|
||||
are called for that resource instance and then the next parametrized
|
||||
resource instance is created and its tests are run. Among other things,
|
||||
this eases testing of applications which create and use global state.
|
||||
|
||||
The following example uses two parametrized funcargs, one of which is
|
||||
scoped on a per-module basis, and all the functions perform ``print`` call s
|
||||
to show the flow of calls::
|
||||
|
||||
# content of test_module.py
|
||||
import pytest
|
||||
|
||||
@pytest.factory(scope="module", params=["mod1", "mod2"])
|
||||
def modarg(request):
|
||||
param = request.param
|
||||
print "create", param
|
||||
def fin():
|
||||
print "fin", param
|
||||
request.addfinalizer(fin)
|
||||
return param
|
||||
|
||||
@pytest.factory(scope="function", params=[1,2])
|
||||
def otherarg(request):
|
||||
return request.param
|
||||
|
||||
def test_0(otherarg):
|
||||
print " test0", otherarg
|
||||
def test_1(modarg):
|
||||
print " test1", modarg
|
||||
def test_2(otherarg, modarg):
|
||||
print " test2", otherarg, modarg
|
||||
|
||||
Let's run the tests in verbose mode and with looking at the print-output::
|
||||
|
||||
$ py.test -v -s test_module.py
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 -- /home/hpk/venv/1/bin/python
|
||||
cachedir: /home/hpk/tmp/doc-exec-423/.cache
|
||||
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
||||
collecting ... collected 8 items
|
||||
|
||||
test_module.py:16: test_0[1] PASSED
|
||||
test_module.py:16: test_0[2] PASSED
|
||||
test_module.py:18: test_1[mod1] PASSED
|
||||
test_module.py:20: test_2[1-mod1] PASSED
|
||||
test_module.py:20: test_2[2-mod1] PASSED
|
||||
test_module.py:18: test_1[mod2] PASSED
|
||||
test_module.py:20: test_2[1-mod2] PASSED
|
||||
test_module.py:20: test_2[2-mod2] PASSED
|
||||
|
||||
========================= 8 passed in 0.02 seconds =========================
|
||||
test0 1
|
||||
test0 2
|
||||
create mod1
|
||||
test1 mod1
|
||||
test2 1 mod1
|
||||
test2 2 mod1
|
||||
fin mod1
|
||||
create mod2
|
||||
test1 mod2
|
||||
test2 1 mod2
|
||||
test2 2 mod2
|
||||
fin mod2
|
||||
|
||||
You can see that the parametrized module-scoped ``modarg`` resource caused
|
||||
an ordering of test execution that lead to the fewest possible "active" resources. The finalizer for the ``mod1`` parametrized resource was executed
|
||||
before the ``mod2`` resource was setup.
|
||||
|
||||
.. currentmodule:: _pytest.python
|
||||
.. _`request`:
|
||||
|
||||
``request``: interacting with test invocation context
|
||||
-------------------------------------------------------
|
||||
|
||||
The ``request`` object may be received by `@pytest.factory`_ or
|
||||
:ref:`@pytest.setup <setup>` marked functions and provides methods
|
||||
|
||||
* to inspect attributes of the requesting test context, such as
|
||||
``function``, ``cls``, ``module``, ``session`` and the pytest
|
||||
``config`` object. A request object passed to a parametrized factory
|
||||
will also carry a ``request.param`` object (A parametrized factory and
|
||||
all of its dependent tests will be called with each of the factory-specified
|
||||
``params``).
|
||||
|
||||
* to add finalizers/teardowns to be invoked when the last
|
||||
test of the requesting test context executes
|
||||
|
||||
.. autoclass:: _pytest.python.FuncargRequest()
|
||||
:members:
|
||||
|
||||
|
||||
.. _`test generators`:
|
||||
|
|
214
doc/en/setup.txt
214
doc/en/setup.txt
|
@ -1,212 +1,12 @@
|
|||
.. _xunitsetup:
|
||||
.. _setup:
|
||||
.. _`setup functions`:
|
||||
.. _`@pytest.setup`:
|
||||
|
||||
``@setup`` functions or: xunit on steroids
|
||||
Page has moved to fixture
|
||||
========================================================
|
||||
|
||||
.. 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.
|
||||
|
||||
During development prior to the pytest-2.3 release the name
|
||||
``pytest.setup`` was used but before the release it was renamed
|
||||
to :ref:`pytest.fixture` mainly to avoid the misconception that there
|
||||
should be a ``pytest.teardown`` as well.
|
||||
|
||||
Please refer to :ref:`pytest.fixture` for information on the new
|
||||
fixture functions.
|
||||
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
|
||||
.. _`old-style xunit`:
|
||||
.. _`classic xunit`:
|
||||
|
||||
Old-style xunit-style setup
|
||||
classic xunit-style setup
|
||||
========================================
|
||||
|
||||
.. note::
|
||||
|
||||
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.
|
||||
supported but it is recommended to rather use :ref:`fixture functions
|
||||
<fixture>` or :ref:`funcargs <resources>` for implementing your
|
||||
needs to prepare and fix the test state for your tests.
|
||||
|
||||
Module level setup/teardown
|
||||
--------------------------------------
|
||||
|
|
Loading…
Reference in New Issue