How to get directory with test from fixture in conftest.py
When I put my fixtures into conftest.py
(which is the preferable way to reuse fixtures within a project), I stumble on one minor issue, is that fixture can’t use __file__
anymore to find directory where file is present.
The normal way in Python to see ‘own’ directory is to use
os.path.dirname(__file__)
(It uses magic variable __file__
with the file name of the current module where code is run). Due to obvious reasons when fixture is moved from (e.g.) /foo/bar/tests_slow/test_slow.py
into /foo/bar/conftest.py
, the content of __file__
is changing.
Why one want to use this ‘magic’?
The key reason is to get files relative to the test file:
.
├── conftest.py
└── some_tests
├── data.json
└── test_some.py
I want to access data.json
from the fixture. When fixture is in the test_some
, I just do os.path.join(os.path.dirname(__file__), "data.json")
. It’s breaks if a fixture is moved to conftest.py
.
Solution
With a bit of digging I found where to find the original path to the test file calling the fixture.
There is a magical fixture request
(not the requests
library! It’s a bit of collision here), which contains all information about the current test and fixture construction. And it has ‘module’, which is module with the test. And that module has own __file__
magic variable, which is the desired information.
Therefore, the content for fixture fixture1
inside conftest.py
is:
@pytest.fixture()
def fixture1(request):
test_dir = os.path.dirname(request.module.__file__)
data_filename = os.path.join(test_dir, "data.json")
with open(data_filename, "r") as f:
return json.load(f)
If you have chain of fixtures, it’s ok to mix-in request
at any point:
@pytest.fixture()
def complicated_fixture(fixture1, fixture2, fixture2, request):
...
Side note: you can pass request
as a fixture into you test, but it’s a bit too much of introspection for my taste.
Update: if you ever decide to go by pytest_generate_tests
way, it’s there too:
metafunc.module.__file__