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.
When a module(a module is a python file) is imported within a Python file, the interpreter first searches for the specified module among its built-in modules. If not found it looks through the list of directories(a directory is a folder that contains related modules) defined by sys. path.
append(mod_directory) to append the path and then open the python interpreter, the directory mod_directory gets added to the end of the list sys. path. If I export the PYTHONPATH variable before opening the python interpreter, the directory gets added to the start of the list.
By default, sys. path is constructed as a concatenation of (1) the current working directory, (2) content of PYTHONPATH environment variable, and (3) a set of default paths supplied by the installed Python interpreter. The error can occur either because the module is indeed missing on your system, or because the sys.
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, or add2virtualenv .
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.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With