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 pytest.fixture to turn it into a fixture.

For example, there is a very nice fixture on stackoverflow that defines a 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 datadir in test_my_module.py that would point to a temporary copy of ./test/test_my_module, including the files file1.dat and 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 say testutils):

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)

Then, in 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)


Comments

No comments

You may format you comment with Markdown. If your comment is a valid contribution, it may be posted on this page. Your email address will not be made public.