2012-10-05 16:21:35 +08:00
|
|
|
.. _fixture:
|
2012-10-07 19:06:17 +08:00
|
|
|
.. _fixtures:
|
2012-10-05 16:21:35 +08:00
|
|
|
.. _`fixture functions`:
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
pytest fixtures: modular, explicit, scalable
|
2012-10-05 16:21:35 +08:00
|
|
|
========================================================
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
.. versionadded:: 2.0, 2.3
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
.. _`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
|
|
|
|
|
2012-10-06 01:20:40 +08:00
|
|
|
pytest allows to create and use test fixtures in a modular and flexible
|
|
|
|
manner, offering dramatic improvements over the classic xUnit style of
|
2012-10-07 19:06:17 +08:00
|
|
|
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 have names and can be
|
|
|
|
activated by referencing them from test functions, modules, classes or
|
|
|
|
whole projects. Fixtures are implemented by *fixture functions* which
|
|
|
|
have full access to the requesting test context and can use other
|
|
|
|
fixtures, allowing a modular and flexible approach to organising
|
|
|
|
and parametrizing fixtures for an application. Complemented by
|
|
|
|
pytest's generic :ref:`parametrize features <parametrize>`, pytest
|
|
|
|
fixtures help to write test suites that scale from simple to complex
|
|
|
|
with minimal effort.
|
|
|
|
|
|
|
|
|
|
|
|
.. _`funcargs`:
|
|
|
|
.. _`funcarg mechanism`:
|
|
|
|
.. _`fixture function`:
|
|
|
|
|
|
|
|
Fixtures as Function arguments
|
|
|
|
-----------------------------------------
|
|
|
|
|
|
|
|
Test functions can receive fixture objects by naming them as an input
|
|
|
|
argument. For each argument name, a matching fixture
|
2012-10-06 01:20:40 +08:00
|
|
|
function will provide a fixture object. This mechanism was already
|
2012-10-07 19:06:17 +08:00
|
|
|
introduced with pytest-2.0 and is also called the *funcarg mechanism*.
|
2012-10-05 16:21:35 +08:00
|
|
|
It allows test functions to easily receive and work against specific
|
|
|
|
pre-initialized application objects without having to care about the
|
2012-10-06 01:20:40 +08:00
|
|
|
details of setup/cleanup procedures. It's a prime example of
|
2012-10-05 16:21:35 +08:00
|
|
|
`dependency injection`_ where fixture functions take the role of the
|
|
|
|
*injector* and test functions are the *consumers* of fixture objects.
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
Let's look at a simple self-contained test module containing
|
|
|
|
a fixture and a test function using it::
|
2012-10-05 16:21:35 +08:00
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
# content of ./test_fixturefuncarg.py
|
2012-10-05 16:21:35 +08:00
|
|
|
import pytest
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
@pytest.fixture
|
2012-10-05 16:21:35 +08:00
|
|
|
def myfuncarg():
|
|
|
|
return 42
|
|
|
|
|
|
|
|
def test_function(myfuncarg):
|
2012-10-07 19:06:17 +08:00
|
|
|
assert myfuncarg == 17 # will fail
|
2012-10-05 16:21:35 +08:00
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
Here, the ``test_function`` needs the ``myfuncarg`` fixture value. pytest
|
|
|
|
will discover and call the ``@pytest.fixture`` marked ``myfuncarg``
|
|
|
|
fixture function. Running the test looks like this::
|
2012-10-05 16:21:35 +08:00
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
$ py.test test_fixturefuncarg.py
|
2012-10-05 16:21:35 +08:00
|
|
|
=========================== test session starts ============================
|
2012-10-07 19:06:17 +08:00
|
|
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19
|
|
|
|
collected 1 items
|
2012-10-05 16:21:35 +08:00
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
test_fixturefuncarg.py F
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
================================= FAILURES =================================
|
|
|
|
______________________________ test_function _______________________________
|
|
|
|
|
|
|
|
myfuncarg = 42
|
|
|
|
|
|
|
|
def test_function(myfuncarg):
|
2012-10-07 19:06:17 +08:00
|
|
|
> assert myfuncarg == 17 # will fail
|
2012-10-05 16:21:35 +08:00
|
|
|
E assert 42 == 17
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
test_fixturefuncarg.py:8: AssertionError
|
2012-10-05 16:21:35 +08:00
|
|
|
========================= 1 failed in 0.01 seconds =========================
|
|
|
|
|
|
|
|
This shows that the test function was called with a ``myfuncarg``
|
2012-10-07 19:06:17 +08:00
|
|
|
value of ``42`` and the assert fails as expected. Here is
|
2012-10-05 16:21:35 +08:00
|
|
|
how py.test comes to call the test function this way:
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
1. pytest :ref:`finds <test discovery>` the ``test_function`` because
|
2012-10-05 16:21:35 +08:00
|
|
|
of the ``test_`` prefix. The test function needs a function argument
|
2012-10-06 01:20:40 +08:00
|
|
|
named ``myfuncarg``. A matching fixture function is discovered by
|
2012-10-05 16:21:35 +08:00
|
|
|
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
|
2012-10-06 01:20:40 +08:00
|
|
|
and you had to use a magic ``pytest_funcarg__NAME`` prefix
|
2012-10-05 16:21:35 +08:00
|
|
|
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
|
|
|
|
-----------------------------------------------------------------
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
By means of a "scope" declaration, a fixture function will
|
|
|
|
only be invoked once per the specified scope. This allows to reduce the
|
|
|
|
number of expensive application object setups and thus helps to speed up
|
|
|
|
test runs. Typical examples are the setup of test databases or
|
|
|
|
establishing required subprocesses or network connections.
|
|
|
|
|
2012-10-05 16:21:35 +08:00
|
|
|
.. regendoc:wipe
|
|
|
|
|
|
|
|
Here is a simple example of a fixture function creating a shared
|
|
|
|
``smtplib.SMTP`` connection fixture which test functions from
|
2012-10-06 01:20:40 +08:00
|
|
|
any test module inside the directory of a ``conftest.py`` file may use::
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
# 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
|
2012-10-07 19:06:17 +08:00
|
|
|
|
2012-10-05 16:21:35 +08:00
|
|
|
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
|
|
|
|
FF
|
|
|
|
================================= FAILURES =================================
|
|
|
|
________________________________ test_ehlo _________________________________
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
smtp = <smtplib.SMTP instance at 0x1c51440>
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
def test_ehlo(smtp):
|
|
|
|
response = smtp.ehlo()
|
|
|
|
assert response[0] == 250
|
|
|
|
assert "merlinux" in response[1]
|
|
|
|
> assert 0 # for demo purposes
|
|
|
|
E assert 0
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
test_module.py:6: AssertionError
|
2012-10-05 16:21:35 +08:00
|
|
|
________________________________ test_noop _________________________________
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
smtp = <smtplib.SMTP instance at 0x1c51440>
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
def test_noop(smtp):
|
|
|
|
response = smtp.noop()
|
|
|
|
assert response[0] == 250
|
|
|
|
> assert 0 # for demo purposes
|
|
|
|
E assert 0
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
test_module.py:11: AssertionError
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
2012-10-06 01:20:40 +08:00
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
Fixtures can interact with the requesting test context
|
|
|
|
-------------------------------------------------------------
|
|
|
|
|
|
|
|
By using the special :ref:`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.
|
2012-10-05 16:21:35 +08:00
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
Further extending the previous ``smtp`` fixture example, let's try to
|
|
|
|
read the server URL from the module namespace, use module-scoping and
|
|
|
|
register a finalizer that closes the smtp connection after the last
|
|
|
|
test finished execution::
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
# content of conftest.py
|
|
|
|
import pytest
|
|
|
|
import smtplib
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
@pytest.fixture(scope="module")
|
2012-10-05 16:21:35 +08:00
|
|
|
def smtp(request):
|
2012-10-07 19:06:17 +08:00
|
|
|
server = getattr(request.module, "smtpserver", "merlinux.eu")
|
|
|
|
smtp = smtplib.SMTP(server)
|
2012-10-05 16:21:35 +08:00
|
|
|
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
|
2012-10-06 01:20:40 +08:00
|
|
|
FF
|
2012-10-07 19:06:17 +08:00
|
|
|
finalizing <smtplib.SMTP instance at 0x1e15a70>
|
|
|
|
|
|
|
|
We see that the ``smtp`` instance is finalized after the two
|
|
|
|
tests using it tests executed. If we had specified ``scope='function'``
|
|
|
|
then fixture setup and cleanup would occur around each single test.
|
|
|
|
Note that the test module itself did not need to change!
|
|
|
|
|
|
|
|
Let's quickly create another test module that actually sets the
|
|
|
|
server URL and has a test to verify the fixture picks it up::
|
|
|
|
|
|
|
|
# content of test_anothersmtp.py
|
|
|
|
|
|
|
|
smtpserver = "mail.python.org" # will be read by smtp fixture
|
|
|
|
|
|
|
|
def test_showhelo(smtp):
|
|
|
|
assert 0, smtp.helo()
|
|
|
|
|
|
|
|
Running it::
|
|
|
|
|
|
|
|
$ py.test -qq --tb=short test_anothersmtp.py
|
|
|
|
F
|
|
|
|
================================= FAILURES =================================
|
|
|
|
______________________________ test_showhelo _______________________________
|
|
|
|
test_anothersmtp.py:5: in test_showhelo
|
|
|
|
> assert 0, smtp.helo()
|
|
|
|
E AssertionError: (250, 'mail.python.org')
|
|
|
|
|
|
|
|
**Test classes, modules or whole projects can make use of
|
|
|
|
one or more fixtures**. All required fixture functions will execute
|
|
|
|
before a test from the specifying context executes. As You can use this
|
|
|
|
to make tests operate from a pre-initialized directory or with
|
|
|
|
certain environment variables or with pre-configured global application
|
|
|
|
settings.
|
|
|
|
|
|
|
|
For example, the Django_ project requires database
|
|
|
|
initialization to be able to import from and use its model objects.
|
|
|
|
For that, the `pytest-django`_ plugin provides fixtures which your
|
|
|
|
project can then easily depend or extend on, simply by referencing the
|
|
|
|
name of the particular fixture.
|
|
|
|
|
|
|
|
|
|
|
|
**Fixture functions have limited 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.
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
Parametrizing a session-shared fixture
|
2012-10-05 16:21:35 +08:00
|
|
|
-----------------------------------------------------------------
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
**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 exhaustive functional tests for components which themselves can be
|
|
|
|
configured in multiple ways.
|
|
|
|
|
2012-10-05 16:21:35 +08:00
|
|
|
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::
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
$ py.test -q test_module.py
|
2012-10-05 16:21:35 +08:00
|
|
|
FFFF
|
|
|
|
================================= FAILURES =================================
|
|
|
|
__________________________ test_ehlo[merlinux.eu] __________________________
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
smtp = <smtplib.SMTP instance at 0x27ae998>
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
def test_ehlo(smtp):
|
|
|
|
response = smtp.ehlo()
|
|
|
|
assert response[0] == 250
|
|
|
|
assert "merlinux" in response[1]
|
|
|
|
> assert 0 # for demo purposes
|
|
|
|
E assert 0
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
test_module.py:6: AssertionError
|
2012-10-05 16:21:35 +08:00
|
|
|
__________________________ test_noop[merlinux.eu] __________________________
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
smtp = <smtplib.SMTP instance at 0x27ae998>
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
def test_noop(smtp):
|
|
|
|
response = smtp.noop()
|
|
|
|
assert response[0] == 250
|
|
|
|
> assert 0 # for demo purposes
|
|
|
|
E assert 0
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
test_module.py:11: AssertionError
|
2012-10-05 16:21:35 +08:00
|
|
|
________________________ test_ehlo[mail.python.org] ________________________
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
smtp = <smtplib.SMTP instance at 0x28395f0>
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
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'
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
test_module.py:5: AssertionError
|
2012-10-05 16:21:35 +08:00
|
|
|
________________________ test_noop[mail.python.org] ________________________
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
smtp = <smtplib.SMTP instance at 0x28395f0>
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
def test_noop(smtp):
|
|
|
|
response = smtp.noop()
|
|
|
|
assert response[0] == 250
|
|
|
|
> assert 0 # for demo purposes
|
|
|
|
E assert 0
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
test_module.py:11: AssertionError
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
|
|
.. _`interdependent fixtures`:
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
Using fixtures from a fixture function
|
2012-10-05 16:21:35 +08:00
|
|
|
----------------------------------------------------------
|
|
|
|
|
|
|
|
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 ============================
|
2012-10-07 19:06:17 +08:00
|
|
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
2012-10-05 16:21:35 +08:00
|
|
|
collecting ... collected 2 items
|
|
|
|
|
2012-10-06 01:20:40 +08:00
|
|
|
test_appsetup.py:12: test_smtp_exists[merlinux.eu] PASSED
|
|
|
|
test_appsetup.py:12: test_smtp_exists[mail.python.org] PASSED
|
2012-10-05 16:21:35 +08:00
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
========================= 2 passed in 0.09 seconds =========================
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
|
2012-10-05 16:21:35 +08:00
|
|
|
.. _`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 ============================
|
2012-10-07 19:06:17 +08:00
|
|
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
2012-10-05 16:21:35 +08:00
|
|
|
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
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
========================= 8 passed in 0.01 seconds =========================
|
2012-10-05 16:21:35 +08:00
|
|
|
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.
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
.. _`usefixtures`:
|
2012-10-05 16:21:35 +08:00
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
|
|
|
|
using fixtures from classes, modules or projects
|
2012-10-05 16:21:35 +08:00
|
|
|
----------------------------------------------------------------------
|
|
|
|
|
|
|
|
.. regendoc:wipe
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
Sometimes test functions do not directly need access to a fixture object.
|
|
|
|
For example, tests may require to operate with an
|
2012-10-05 16:21:35 +08:00
|
|
|
empty directory as the current working directory. Here is how you can
|
2012-10-07 19:06:17 +08:00
|
|
|
can use the standard `tempfile <http://docs.python.org/library/tempfile.html>`_ and pytest fixtures to
|
|
|
|
achieve it. We separate the creation of the fixture into a conftest.py
|
|
|
|
file::
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
# content of conftest.py
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
import tempfile
|
|
|
|
import os
|
|
|
|
|
|
|
|
@pytest.fixture()
|
|
|
|
def cleandir():
|
|
|
|
newpath = tempfile.mkdtemp()
|
|
|
|
os.chdir(newpath)
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
and declare its use in a test module via a ``usefixtures`` marker::
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
# content of test_setenv.py
|
|
|
|
import os
|
|
|
|
import pytest
|
|
|
|
|
2012-10-06 01:20:40 +08:00
|
|
|
@pytest.mark.usefixtures("cleandir")
|
2012-10-05 16:21:35 +08:00
|
|
|
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()) == []
|
|
|
|
|
2012-10-06 01:20:40 +08:00
|
|
|
Due to the ``usefixtures`` marker, the ``cleandir`` fixture
|
2012-10-07 19:06:17 +08:00
|
|
|
will be required for the execution of each test method, just as if
|
2012-10-05 16:21:35 +08:00
|
|
|
you specified a "cleandir" function argument to each of them. Let's run it
|
2012-10-07 19:06:17 +08:00
|
|
|
to verify our fixture is activated and the tests pass::
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
$ py.test -q
|
2012-10-06 01:20:40 +08:00
|
|
|
..
|
2012-10-05 16:21:35 +08:00
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
You can specify multiple fixtures like this::
|
2012-10-05 16:21:35 +08:00
|
|
|
|
2012-10-06 01:20:40 +08:00
|
|
|
@pytest.mark.usefixtures("cleandir", "anotherfixture")
|
2012-10-05 16:21:35 +08:00
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
and you may specify fixture usage at the test module level, using
|
2012-10-05 16:21:35 +08:00
|
|
|
a generic feature of the mark mechanism::
|
|
|
|
|
2012-10-06 01:20:40 +08:00
|
|
|
pytestmark = pytest.mark.usefixtures("cleandir")
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
Lastly you can put fixtures required by all tests in your project
|
|
|
|
into an ini-file::
|
|
|
|
|
|
|
|
# content of pytest.ini
|
|
|
|
|
|
|
|
[pytest]
|
2012-10-06 01:20:40 +08:00
|
|
|
usefixtures = cleandir
|
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
.. _`autoactive fixtures`:
|
2012-10-05 16:21:35 +08:00
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
autoactive fixtures (xUnit setup on steroids)
|
2012-10-05 16:21:35 +08:00
|
|
|
----------------------------------------------------------------------
|
|
|
|
|
|
|
|
.. regendoc:wipe
|
|
|
|
|
|
|
|
Occasionally, you may want to have fixtures get invoked automatically
|
2012-10-07 19:06:17 +08:00
|
|
|
without a `usefixtures`_ or `funcargs`_ reference. As a practical
|
|
|
|
example, suppose we have a database fixture which has a
|
|
|
|
begin/rollback/commit architecture and we want to automatically surround
|
|
|
|
each test method by a transaction and a rollback. Here is a dummy
|
|
|
|
self-contained implementation of this idea::
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
# content of test_db_transact.py
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
2012-10-08 17:22:31 +08:00
|
|
|
class DB:
|
2012-10-05 16:21:35 +08:00
|
|
|
def __init__(self):
|
2012-10-06 01:20:40 +08:00
|
|
|
self.intransaction = []
|
|
|
|
def begin(self, name):
|
|
|
|
self.intransaction.append(name)
|
|
|
|
def rollback(self):
|
|
|
|
self.intransaction.pop()
|
2012-10-05 16:21:35 +08:00
|
|
|
|
2012-10-08 17:22:31 +08:00
|
|
|
@pytest.fixture(scope="module")
|
|
|
|
def db():
|
|
|
|
return DB()
|
|
|
|
|
2012-10-05 16:21:35 +08:00
|
|
|
class TestClass:
|
2012-10-06 01:20:40 +08:00
|
|
|
@pytest.fixture(autoactive=True)
|
2012-10-05 16:21:35 +08:00
|
|
|
def transact(self, request, db):
|
2012-10-06 01:20:40 +08:00
|
|
|
db.begin(request.function.__name__)
|
2012-10-05 16:21:35 +08:00
|
|
|
request.addfinalizer(db.rollback)
|
|
|
|
|
|
|
|
def test_method1(self, db):
|
2012-10-06 01:20:40 +08:00
|
|
|
assert db.intransaction == ["test_method1"]
|
|
|
|
|
|
|
|
def test_method2(self, db):
|
|
|
|
assert db.intransaction == ["test_method2"]
|
2012-10-05 16:21:35 +08:00
|
|
|
|
2012-10-08 17:22:31 +08:00
|
|
|
The class-level ``transact`` fixture is marked with *autoactive=true*
|
|
|
|
which implies that all test methods in the class will use this fixture
|
|
|
|
without a need to specify it.
|
2012-10-05 16:21:35 +08:00
|
|
|
|
2012-10-06 01:20:40 +08:00
|
|
|
If we run it, we get two passing tests::
|
|
|
|
|
|
|
|
$ py.test -q
|
|
|
|
..
|
2012-10-05 16:21:35 +08:00
|
|
|
|
2012-10-06 01:20:40 +08:00
|
|
|
And here is how autoactive fixtures work in other scopes:
|
2012-10-05 16:21:35 +08:00
|
|
|
|
2012-10-06 01:20:40 +08:00
|
|
|
- if an autoactive fixture is defined in a test module, all its test
|
|
|
|
functions automatically use it.
|
2012-10-05 16:21:35 +08:00
|
|
|
|
2012-10-06 01:20:40 +08:00
|
|
|
- if an autoactive fixture is defined in a conftest.py file then all tests in
|
|
|
|
all test modules belows its directory will invoke the fixture.
|
2012-10-05 16:21:35 +08:00
|
|
|
|
2012-10-06 01:20:40 +08:00
|
|
|
- lastly, and **please use that with care**: if you define an autoactive
|
2012-10-05 16:21:35 +08:00
|
|
|
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
|
2012-10-06 01:20:40 +08:00
|
|
|
anyway works in the presence of certain settings e. g. in the ini-file. Such
|
|
|
|
a global fixture should always quickly determine if it should do
|
2012-10-05 16:21:35 +08:00
|
|
|
any work and avoid expensive imports or computation otherwise.
|
|
|
|
|
2012-10-08 17:22:31 +08:00
|
|
|
Note that the above ``transact`` fixture may very well be a fixture that
|
|
|
|
you want to make available in your project without having it generally
|
|
|
|
active. The canonical way to do that is to put the transact definition
|
|
|
|
into a conftest.py file without using ``autoactive``::
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
# content of conftest.py
|
|
|
|
@pytest.fixture()
|
|
|
|
def transact(self, request, db):
|
|
|
|
db.begin()
|
|
|
|
request.addfinalizer(db.rollback)
|
|
|
|
|
2012-10-08 17:22:31 +08:00
|
|
|
and then e.g. have a TestClass using it by declaring the need::
|
2012-10-05 16:21:35 +08:00
|
|
|
|
2012-10-06 01:20:40 +08:00
|
|
|
@pytest.mark.usefixtures("transact")
|
2012-10-05 16:21:35 +08:00
|
|
|
class TestClass:
|
|
|
|
def test_method1(self):
|
|
|
|
...
|
|
|
|
|
2012-10-08 17:22:31 +08:00
|
|
|
All test methods in this TestClass will use the transaction fixture while
|
|
|
|
other test classes or functions will not do so unless they also add
|
|
|
|
a ``transact`` reference.
|
2012-10-05 16:21:35 +08:00
|
|
|
|
2012-10-07 19:06:17 +08:00
|
|
|
controlled visibility of fixture functions
|
|
|
|
----------------------------------------------------
|
|
|
|
|
|
|
|
If during implementing your tests you realize that you
|
|
|
|
want to use a fixture function from multiple test files you can move it
|
|
|
|
to a :ref:`conftest.py <conftest.py>` file or even separately installable
|
|
|
|
:ref:`plugins <plugins>` without changing test code. The discovery of
|
|
|
|
fixtures functions starts at test classes, then test modules, then
|
|
|
|
``conftest.py`` files and finally builtin and third party plugins.
|
|
|
|
|
2012-10-05 16:21:35 +08:00
|
|
|
.. currentmodule:: _pytest.python
|
|
|
|
|
|
|
|
.. _`@pytest.fixture`:
|
2012-10-07 19:06:17 +08:00
|
|
|
.. _`pytest.fixture`:
|
2012-10-05 16:21:35 +08:00
|
|
|
|
|
|
|
``@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
|
|
|
|
|
2012-10-05 20:24:44 +08:00
|
|
|
.. autoclass:: _pytest.python.FixtureRequest()
|
2012-10-05 16:21:35 +08:00
|
|
|
:members:
|
|
|
|
|