175 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
			
		
		
	
	
			175 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
.. _about-fixtures:
 | 
						|
 | 
						|
About fixtures
 | 
						|
===============
 | 
						|
 | 
						|
.. seealso:: :ref:`how-to-fixtures`
 | 
						|
.. seealso:: :ref:`Fixtures reference <reference-fixtures>`
 | 
						|
 | 
						|
pytest fixtures are designed to be explicit, modular and scalable.
 | 
						|
 | 
						|
What fixtures are
 | 
						|
-----------------
 | 
						|
 | 
						|
In testing, a `fixture <https://en.wikipedia.org/wiki/Test_fixture#Software>`_
 | 
						|
provides a defined, reliable and consistent context for the tests. This could
 | 
						|
include environment (for example a database configured with known parameters)
 | 
						|
or content (such as a dataset).
 | 
						|
 | 
						|
Fixtures define the steps and data that constitute the *arrange* phase of a
 | 
						|
test (see :ref:`test-anatomy`). In pytest, they are functions you define that
 | 
						|
serve this purpose. They can also be used to define a test's *act* phase; this
 | 
						|
is a powerful technique for designing more complex tests.
 | 
						|
 | 
						|
The services, state, or other operating environments set up by fixtures are
 | 
						|
accessed by test functions through arguments. For each fixture used by a test
 | 
						|
function there is typically a parameter (named after the fixture) in the test
 | 
						|
function's definition.
 | 
						|
 | 
						|
We can tell pytest that a particular function is a fixture by decorating it with
 | 
						|
:py:func:`@pytest.fixture <pytest.fixture>`. Here's a simple example of
 | 
						|
what a fixture in pytest might look like:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    import pytest
 | 
						|
 | 
						|
 | 
						|
    class Fruit:
 | 
						|
        def __init__(self, name):
 | 
						|
            self.name = name
 | 
						|
 | 
						|
        def __eq__(self, other):
 | 
						|
            return self.name == other.name
 | 
						|
 | 
						|
 | 
						|
    @pytest.fixture
 | 
						|
    def my_fruit():
 | 
						|
        return Fruit("apple")
 | 
						|
 | 
						|
 | 
						|
    @pytest.fixture
 | 
						|
    def fruit_basket(my_fruit):
 | 
						|
        return [Fruit("banana"), my_fruit]
 | 
						|
 | 
						|
 | 
						|
    def test_my_fruit_in_basket(my_fruit, fruit_basket):
 | 
						|
        assert my_fruit in fruit_basket
 | 
						|
 | 
						|
Tests don't have to be limited to a single fixture, either. They can depend on
 | 
						|
as many fixtures as you want, and fixtures can use other fixtures, as well. This
 | 
						|
is where pytest's fixture system really shines.
 | 
						|
 | 
						|
 | 
						|
Improvements over xUnit-style setup/teardown functions
 | 
						|
-----------------------------------------------------------
 | 
						|
 | 
						|
pytest fixtures offer dramatic improvements over the classic xUnit
 | 
						|
style of setup/teardown functions:
 | 
						|
 | 
						|
* fixtures have explicit names and are activated by declaring their use
 | 
						|
  from test functions, modules, classes or whole projects.
 | 
						|
 | 
						|
* fixtures are implemented in a modular manner, as each fixture name
 | 
						|
  triggers a *fixture function* which can itself use other fixtures.
 | 
						|
 | 
						|
* fixture management scales from simple unit to complex
 | 
						|
  functional testing, allowing to parametrize fixtures and tests according
 | 
						|
  to configuration and component options, or to re-use fixtures
 | 
						|
  across function, class, module or whole test session scopes.
 | 
						|
 | 
						|
* teardown logic can be easily, and safely managed, no matter how many fixtures
 | 
						|
  are used, without the need to carefully handle errors by hand or micromanage
 | 
						|
  the order that cleanup steps are added.
 | 
						|
 | 
						|
In addition, pytest continues to support :ref:`xunitsetup`.  You can mix
 | 
						|
both styles, moving incrementally from classic to new style, as you
 | 
						|
prefer.  You can also start out from existing :ref:`unittest.TestCase
 | 
						|
style <unittest.TestCase>` or :ref:`nose based <nosestyle>` projects.
 | 
						|
 | 
						|
 | 
						|
 | 
						|
Fixture errors
 | 
						|
--------------
 | 
						|
 | 
						|
pytest does its best to put all the fixtures for a given test in a linear order
 | 
						|
so that it can see which fixture happens first, second, third, and so on. If an
 | 
						|
earlier fixture has a problem, though, and raises an exception, pytest will stop
 | 
						|
executing fixtures for that test and mark the test as having an error.
 | 
						|
 | 
						|
When a test is marked as having an error, it doesn't mean the test failed,
 | 
						|
though. It just means the test couldn't even be attempted because one of the
 | 
						|
things it depends on had a problem.
 | 
						|
 | 
						|
This is one reason why it's a good idea to cut out as many unnecessary
 | 
						|
dependencies as possible for a given test. That way a problem in something
 | 
						|
unrelated isn't causing us to have an incomplete picture of what may or may not
 | 
						|
have issues.
 | 
						|
 | 
						|
Here's a quick example to help explain:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    import pytest
 | 
						|
 | 
						|
 | 
						|
    @pytest.fixture
 | 
						|
    def order():
 | 
						|
        return []
 | 
						|
 | 
						|
 | 
						|
    @pytest.fixture
 | 
						|
    def append_first(order):
 | 
						|
        order.append(1)
 | 
						|
 | 
						|
 | 
						|
    @pytest.fixture
 | 
						|
    def append_second(order, append_first):
 | 
						|
        order.extend([2])
 | 
						|
 | 
						|
 | 
						|
    @pytest.fixture(autouse=True)
 | 
						|
    def append_third(order, append_second):
 | 
						|
        order += [3]
 | 
						|
 | 
						|
 | 
						|
    def test_order(order):
 | 
						|
        assert order == [1, 2, 3]
 | 
						|
 | 
						|
 | 
						|
If, for whatever reason, ``order.append(1)`` had a bug and it raises an exception,
 | 
						|
we wouldn't be able to know if ``order.extend([2])`` or ``order += [3]`` would
 | 
						|
also have problems. After ``append_first`` throws an exception, pytest won't run
 | 
						|
any more fixtures for ``test_order``, and it won't even try to run
 | 
						|
``test_order`` itself. The only things that would've run would be ``order`` and
 | 
						|
``append_first``.
 | 
						|
 | 
						|
 | 
						|
Sharing test data
 | 
						|
-----------------
 | 
						|
 | 
						|
If you want to make test data from files available to your tests, a good way
 | 
						|
to do this is by loading these data in a fixture for use by your tests.
 | 
						|
This makes use of the automatic caching mechanisms of pytest.
 | 
						|
 | 
						|
Another good approach is by adding the data files in the ``tests`` folder.
 | 
						|
There are also community plugins available to help to manage this aspect of
 | 
						|
testing, e.g. :pypi:`pytest-datadir` and :pypi:`pytest-datafiles`.
 | 
						|
 | 
						|
.. _fixtures-signal-cleanup:
 | 
						|
 | 
						|
A note about fixture cleanup
 | 
						|
----------------------------
 | 
						|
 | 
						|
pytest does not do any special processing for :data:`SIGTERM <signal.SIGTERM>` and
 | 
						|
:data:`SIGQUIT <signal.SIGQUIT>` signals (:data:`SIGINT <signal.SIGINT>` is handled naturally
 | 
						|
by the Python runtime via :class:`KeyboardInterrupt`), so fixtures that manage external resources which are important
 | 
						|
to be cleared when the Python process is terminated (by those signals) might leak resources.
 | 
						|
 | 
						|
The reason pytest does not handle those signals to perform fixture cleanup is that signal handlers are global,
 | 
						|
and changing them might interfere with the code under execution.
 | 
						|
 | 
						|
If fixtures in your suite need special care regarding termination in those scenarios,
 | 
						|
see :issue:`this comment <5243#issuecomment-491522595>` in the issue
 | 
						|
tracker for a possible workaround.
 |