Using pytest where test in subfolder

Question:

I’m using python pytest to run my unit tests.
My project folders are:

Main – contains data file: A.txt

MainTests – the folder from which I run pytest

MainTestsA_test – folder that contains a test file

The test in A_test folder uses the file A.txt (that is in Main folder).

My problem is that when I run py.test the test fails because it can’t find A.txt.

I found out that it is because pytest uses the path MainTest when running the test instead of changing the path to MainTestsA_test (I’m using relative path when opening A.txt inside the test file)

My question: is there a way to make pytest change directory to the folder of the test it executes for each test? so that relative paths inside the tests will still work?

Is there some other generic way to solve it? (I don’t want to change everything to absolute paths or something like this, also this is an example, in real life I have several hundreds tests).

Thank you,

Noam

Asked By: Noam

||

Answers:

Well I kind of solved it, not sure it is the best way but it is working:

In each of the tests:

  1. I check if the test is being executed from it directory or from MainTests
  2. If it is being executed from MainTests then I chdir to MainTestsA_test

I do this under the def setUpClass method.

For example:

@classmethod
def setUpClass(cls):
    if (os.path.exists(os.path.join(os.curdir, "A_test"))):
        os.chdir("A_test")

This makes the test pass no matter if it is executed from Tests folder (with pytest) or from A_test folder (through pycharm)

Answered By: Noam

Option A — Minimal solution

At the root of your project, create a file called tests.py with the following in it

import os, pathlib
import pytest

os.chdir( pathlib.Path.cwd() / 'Tests' )

pytest.main()

You can then use the command python tests.py to run the tests.


Option B — With batch/bash test runners

For those who prefer using batch/bash to run scripts, we can change directories within batch/bash, and then call a Python script that runs the pytest framework. To do this, create the following scripts in the project folder.

test.bat (for Windows)

@echo off

cd /d %~dp0Tests
python %~dp0Tests/runner.py %*
cd /d %~dp0

test.sh (for Linux)

cd $PWD/Tests
python runner.py $@
cd $PWD

And then in the Tests folder, create a file called runner.py with the following

import pathlib, sys
import pytest

cwd = pathlib.Path.cwd()

# Add the project's root directory to the system path
sys.path.append(str( cwd.parent ))

# This is optional, but you can add a lib directory 
# To the system path for tests to be able to use
sys.path.append(str( cwd / 'lib' ))

pytest.main()

If your directory structure includes some type of lib folder within your Tests folder, we can instruct pytest to ignore it by creating a pytest.ini config file with the following.

[pytest]
norecursedirs = lib

Under this scenario, your directory/file structure would end up being:

root
├── test.bat
├── test.sh
├── Main
└── Tests
    ├── runner.py
    ├── pytest.ini # Optional pytest config file
    ├── lib # Optional, contains helper modules for the tests
    ├── tests # Tests go here
    └── # Or, in the OPs case, you could also place all of your tests here


Additional Comments

The methods above aren’t the typical way of running pytest, but I prefer using pytest.main() because it allows us to:

  • Have any directory structure.
  • Execute code before the test runner starts.
  • And you can still pass in command line options, and it will behave exactly the same as if you were running the pytest command directly.

Assuming you need your root Main in the sys.path.

Giving your current dir is Main/:

$python -m pytest Tests/A_test

This will append Main to the sys.path and run tests in the A_test subdirectory.
More about pythonpath and pytest relationship here: http://doc.pytest.org/en/latest/pythonpath.html#pythonpath

Answered By: klapshin

Adding __init__.py to the package of the tests worked for me. All test are executed afterwards.

Answered By: shalama

A "generic" (i.e. often-used) way to solve this is to install your package as an editable install:

> py -m pip -e .

This requires first adding a __init__.py in your folder (to turn it into a package) before running the above command.

Aside from this, I think the "truest" answer (perhaps subject to opinion) comes from the pytest docs themselves: the testpaths configuration option can be set in a setup.cfg, pytest.ini, tox.ini, or pyroject.toml file.

Answered By: A. Hendry
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.