Reputation: 195
In pytest, my testing script compares the calculated results with baseline results which are loaded via
SCRIPTLOC = os.path.dirname(__file__)
TESTBASELINE = os.path.join(SCRIPTLOC, 'baseline', 'baseline.csv')
baseline = pandas.DataFrame.from_csv(TESTBASELINE)
Is there a non-boilerplate way to tell pytest to start looking from the root directory of the script rather than get the absolute location through SCRIPTLOC?
Upvotes: 4
Views: 9040
Reputation: 590
If you are simply looking for the pytest equivalent of using __file__
, you can add the request
fixture to your test and use request.path
From the docs:
class FixtureRequest ... property path: Path Path where the test function was collected.
So an example might look like:
def test_script_loc(request):
baseline = request.path.parent.joinpath('baseline', 'baseline.cvs')
print(baseline)
If you're wanting to avoid boilerplate though, you're not going to gain much from doing this (assuming I understand what you mean by 'non-boilerplate')
Personally, I think using the fixture is more explicit (within pytest idioms), but I prefer to wrap the request manipulation in another fixture so I know that I'm explicitly grabbing sample test data just by looking at the method signature of a test.
Here's a snippet I use (modified to match your question, I use a subdirectory hierarchy):
# in conftest.py
import pytest
from pathlib import Path
@pytest.fixture(scope="module")
def script_loc(request):
'''Return the directory of the currently running test script'''
return request.path.parent
And sample usage
def test_script_loc(script_loc):
baseline = script_loc.joinpath('baseline/baseline.cvs')
print(baseline)
I've long since stopped supporting legacy Python and instead use pathlib
.
Given that, I no longer return LocalPath
objects nor depend on their API, but occassionally I still need to support platforms with pytest<=6.0
which do not yet have the request.path
property.
The pytest team is also planning to eventually deprecate py.path
and is porting their internals to the standard library pathlib
. This started as early as pytest 3.9.0 with the introduction of tmp_path
, though the actual removal of LocalPath
attributes may not happen for some time.
Until you can upgrade to pytest>=7.0
, it's easy enough to convert the LocalPath
to a Path
ourselves.
Here's a variant of the above example using a Path
object, tweak as it suits you:
# in conftest.py
import pytest
from pathlib import Path
@pytest.fixture(scope="module")
def script_loc(request):
'''Return the directory of the currently running test script'''
return Path(request.fspath).parent
And sample usage
def test_script_loc(script_loc):
baseline = script_loc.joinpath('baseline/baseline.cvs')
print(baseline)
For older versions of pytest that depend on py.path
and/or required Python 2.x support, the original version of this answer used the request.fspath
attribute to return an instance of py.path.LocalPath
.
From the docs:
class FixtureRequest ... fspath the file system path of the test module which collected this test.
So an example might look like:
def test_script_loc(request):
baseline = os.path.join(request.fspath.dirname, 'baseline', 'baseline.cvs')
print(baseline)
The fixture variant might appear as follows:
# in conftest.py
import pytest
@pytest.fixture(scope="module")
def script_loc(request):
'''Return the directory of the currently running test script'''
# uses .join instead of .dirname so we get a LocalPath object instead of
# a string. LocalPath.join calls normpath for us when joining the path
return request.fspath.join('..')
And sample usage:
def test_script_loc(script_loc):
baseline = script_loc.join('baseline/baseline.cvs')
print(baseline)
Upvotes: 14