4

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?

3
  • thisis completely unrelated to pytest, its entirely in your own code, thus entirely your responsibility Commented Jan 3, 2016 at 13:56
  • Maybe I am asking this wrongly. How does one reference relative (as in relative to the test script) directories in pytest? I put in the code to show what I have been doing to get around it. Commented Jan 4, 2016 at 10:45
  • 1
    there currently isn't, that's a standard python problem - stuff like pkgutil/pkg_ressources can help Commented Jan 4, 2016 at 16:50

1 Answer 1

14

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)

pytest <= 6 compatibility

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)

Legacy py.path

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)
Sign up to request clarification or add additional context in comments.

2 Comments

The second example you posted doesn't seem to work, I get this error: Fixture "get_test_workbook" called directly. Fixtures are not meant to be called directly,
@RaleighL. that error should only occur if you both forgot to add the fixture as an argument in your test function's signature and also then called the fixture directly in the function body.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.