The tmpdir
fixture in py.test uses the function
scope and thus isn't available in a fixture with a broader scope such as session
. However, this would be useful for some cases such as setting up a temporary PostgreSQL server (which of course shouldn't be recreated for each test).
Is there any clean way to get a temporary folder for a broader scope that does not involve writing my own fixture and accessing internal APIs of py.test?
You can put fixtures into individual test files, but to share fixtures among multiple test files, you need to use a conftest.py file somewhere centrally located for all of the tests. For the Tasks project, all of the fixtures will be in tasks_proj/tests/conftest.py. From there, the fixtures can be shared by any test.
Pytest only caches one instance of a fixture at a time, which means that when using a parametrized fixture, pytest may invoke a fixture more than once in the given scope.
It is possible to apply a fixture to all of the tests in a hierarchy, even if the tests don't explicitly request a fixture, by passing autouse=True to the @pytest. fixture decorator. This is useful when we need to apply a side-effect before and/or after each test unconditionally. @pytest.fixture(autouse=True)
Fixtures include an optional parameter called scope, which controls how often a fixture gets set up and torn down. The scope parameter to @pytest. fixture() can have the values of function, class, module, or session. The default scope is function.
Since pytest release 2.8 and above the session-scoped tmpdir_factory
fixture is available. See the example below from the documentation.
# contents of conftest.py import pytest @pytest.fixture(scope='session') def image_file(tmpdir_factory): img = compute_expensive_image() fn = tmpdir_factory.mktemp('data').join('img.png') img.save(str(fn)) return fn # contents of test_image.py def test_histogram(image_file): img = load_image(image_file) # compute and test histogram
Unfortunately there is currently no way (2014) of doing this nicely. In the future py.test will introduce a new "any" scope or something similar for this, but that's the future.
Right now you have to do this manually yourself. However as you note you lose quite a few nice features: symlinks in /tmp to the last test, auto cleanup after a few test runs, sensibly named directories etc. If the directory is not too expensive I usually combine a session and function scoped fixture in the following way:
@pytest.fixture(scope='session') def session_dir(request): temp_dir = py.path.local(tempfile.mkdtemp()) request.addfinalizer(lambda: folder.remove(rec=1)) # Any extra setup here return temp_dir @pytest.fixture def temp_dir(session_dir, tmpdir): session_dir.copy(tmpdir) return tmpdir
This creates a temporary directory which gets cleaned up after a test run, however for each test which actually needs it (by requesting temp_dir
) gets a copy which is saved with the tmpdir semantics.
If tests actually need to share state via this directory then the finalizer of temp_dir
would have to copy things back to the session_dir
. This is however not a very good idea since it makes the tests reliant on the execution order and would also cause problems when using pytest-xdist.
I add a finalizer when I want to delete all temporary folders created in session.
_tmp_factory = None
@pytest.fixture(scope="session")
def tmp_factory(request, tmpdir_factory):
global _tmp_factory
if _tmp_factory is None:
_tmp_factory = tmpdir_factory
request.addfinalizer(cleanup)
return _tmp_factory
def cleanup():
root = _tmp_factory.getbasetemp().strpath
print "Cleaning all temporary folders from %s" % root
shutil.rmtree(root)
def test_deleting_temp(tmp_factory):
root_a = tmp_factory.mktemp('A')
root_a.join('foo.txt').write('hello world A')
root_b = tmp_factory.mktemp('B')
root_b.join('bar.txt').write('hello world B')
for root, _, files in os.walk(tmp_factory.getbasetemp().strpath):
for name in files:
print(os.path.join(root, name))
The output should be like:
/tmp/pytest-of-agp/pytest-0/.lock
/tmp/pytest-of-agp/pytest-0/A0/foo.txt
/tmp/pytest-of-agp/pytest-0/B0/bar.txt
Cleaning all temporary folders from /tmp/pytest-of-agp/pytest-0
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With