Ensuring py.test includes the application directory in sys.path
PythonUnit TestingPytestPython Problem Overview
I have a project directory structure as follows (which I think is pretty standard):
my_project
setup.py
mypkg
__init__.py
foo.py
tests
functional
test_f1.py
unit
test_u1.py
I'm using py.test for my testing framework, and I'd expect to be able to run py.test tests
when in the my_project
directory to run my tests. This does indeed work, until I try to import my application code using (for example) import mypkg
in a test. At that point, I get the error "No module named mypkg". On doing a bit of investigation, it appears that py.test
runs the tests with the directory of the test file in sys.path
, but not the directory that py.test
was run from.
In order to work around this, I have added a conftest.py
file to my tests
directory, containing the following code:
import sys, os
# Make sure that the application source directory (this directory's parent) is
# on sys.path.
here = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, here)
This seems to work, but is it a good way of making sure that the tests see the application code? Is there a better way of achieving this, or am I doing something wrong in how I have my project structured?
I've looked at some other projects that use py.test
(for example, pip
) but I can't see code that does anything like this, and yet running py.test tests
seems to work there. I don't know quite why, but I'm worried that they may have achieved the same result in a simpler way.
I've looked in the py.test
documentation, but I can't see an explanation of this problem or what the recommended approach is to deal with it.
Python Solutions
Solution 1 - Python
As you say yourself py.test basically assumes you have the PYTHONPATH setup up correctly. There are several ways of achieving this:
-
Give your project a setup.py and use
pip install -e .
in a virtualenv for this project. This is probably the standard method. -
As a variation on this if you have a virtualenv but no setup.py use your venv's facility to add the projects directory on sys.path, e.g.
pew add .
if you use pew, oradd2virtualenv .
if you use virtualenv and the extensions of virtualenvwrapper. -
If you always like the current working directory on sys.path you can simply always export
PYTHONPATH=''
in your shell. That is ensure the empty string on on sys.path which python will interpret as the current working direcotry. This is potentially a security hazard though. -
My own favourite hack, abuse how py.test loads conftest files: put an empty
conftest.py
in the project's top-level directory.
The reason for py.test to behave this way is to make it easy to run the tests in a tests/ directory of a checkout against an installed package. If it would unconditionally add the project directory to the PYTHONPATH then this would not be possible anymore.
Solution 2 - Python
The easy way of doing it is, in terminal/cmd change directory to where the parent directory is, (e.g. in this case cd C:/.../my_project
).
Then run:
python -m pytest --cov=mypkg tests
No need to mess with the PYTHONPATH
environment variable.
By running with python -m pytest
, it automatically adds the current directory to sys.path
.
Solution 3 - Python
The answer is actually much easier, as seen here.
All you need to do is add an __init__.py
to your test directory and each of its sub directories, like so;
tests/__init__.py
tests/functional/__init__.py
tests/unit/__init__.py
Solution 4 - Python
Run pytest referencing a single/multiple packages
For 1 package
PYTHONPATH=$(pwd)/mypkg/ python3 -m pytest
# or
PYTHONPATH=$(pwd)/mypkg/ python3 -m pytest path/to/tests
For 2 packages (pkg1 pkg2)
# Use ; as a separator in windows
PYTHONPATH=/path/to/pkg1/:/path/to/pkg2/ python3 -m pytest tests