The excellent pytest framework uses the concept of fixtures for setup/teardown, and for sharing data between tests. However, fixtures can come with a bit of “magic” the runs counter to Python’s tenet that explicit is better than implicit. Specifically, built-in fixtures (or fixtures provided by plugins) are used without being explicitly imported. Thus, a developer is likely to be confused unless they are sufficiently familiar with the way pytest works.
The situation is even graver when we want to share fixtures between multiple
test modules. The recommended method for handling this involves
defining the fixtures in a file
conftest.py that is placed in the root of the
folder containing all the tests, and will be found and processed by pytest
automatically. Again, a developer must be aware of this behavior to figure out
where a fixture is coming from.
Sadly, there is no way to explicitly import fixtures into a testing module. What
we can do, however, is to define “proto-fixtures” as part of a project. A
proto-fixture is a routine that only needs to be decorated with
to turn it into a fixture.
For example, there is a very nice fixture on stackoverflow that
datadir: for every test module, we can have a folder with the exact
same name as the module, holding files that should be accessible to the test.
That is, with the following layout of files,
. └── test ├── test_my_module │ ├── file1.dat │ └── file2.dat └── test_my_module.py
we can have a fixture
test_my_module.py that would point to a
temporary copy of
./test/test_my_module, including the files
file2.dat. This is clearly something we would want to have available in all of
our testing modules! So, we define the following routine in some module (let’s
import os from distutils import dir_util def datadir(tmpdir, request): '''Proto-fixture responsible for searching a folder with the same name as a test module and, if available, moving all contents to a temporary directory so tests can use them freely. ''' filename = request.module.__file__ test_dir, _ = os.path.splitext(filename) if os.path.isdir(test_dir): dir_util.copy_tree(test_dir, str(tmpdir)) return str(tmpdir)
test_my_module.py, and any other testing module, we could use this
in a very clear and explicit form, as:
import testutil import os from pytest import fixture datadir = pytest.fixture(testutil.datadir) def test_my_function(datadir): assert os.path.isdir(datadir)