2024-02-10 PATH AND VISIBILITY MYSTERIES EXPLAINED
I originally answered this in October 2021. I had struggled with this problem for ages. As described in my original answer below NONE of the solutions here worked. Over 2 years I got 24 upvotes (and 2 downvotes) so it seemed like others were having similar intractable difficulties. I had reluctantly concluded that I had to manipulate sys.path
to get pytest to be able to "see" the files that I thought it should see.
I was wrong and failed to understand something about paths. And CRUCIALLY this involves the running of a project (as most of my projects are) where the application is run by using this command (in W10):
> python src\core
In other words, from the start, the CWD is not the current (project root) directory, but a subdirectory [project root directory]\src\core (which contains file __main__.py
)
This is where the problem arose. If you are not in fact running a file in the root directory of your project you need to be aware that putting the tests
directory in your root directory will plunge you into a world of pain and bafflement: sibling .py files will bafflingly not be "found" in your main "run" directory, i.e. where CWD is, i.e. by virtue of your command line.
Instead, in my case, I need the root "tests" directory to be a subdirectory of src\core (relative to the root directory of my project).
As a more general rule: wherever the CWD of your project is as you run it as an application is the place where you must put your "tests" directory.
Hope this helps someone.
-------------- MY ORIGINAL NAIVE, INCORRECT ANSWER OF 2021-10:
ANOTHER SUGGESTION
I explored this question and various others on SO and elsewhere... all the stuff about adding (or removing) an empty __init__.py
in and/or conftest.py in various parts of the project directory structure, all the stuff about PYTHONPATH, etc., etc.: NONE of these solutions worked for me, in what is actually a very simple situation, and which shouldn't be causing any grief.
I regard this as a flaw in pytest's current setup. In fact I got a message recently from someone on SO who clearly knew his stuff. He said that pytest is not designed to work with (as per Java/Groovy/Gradle) separate "src" and "test" directory structures, and that test files should be mingled in amongst the application directories and files. This perhaps goes some way to providing an explanation ... however, tests, particularly integration/functional tests, don't always necessarily correspond neatly to particular directories, and I think pytest should give users more choice in this regard.
Structure of my project:
project_root
src
core
__init__.py
__main__.py
my_other_file.py
tests
basic_tests
test_first_tests.py
The import problem posed: very simply, __main__.py
has a line import my_other_file
. This (not surprisingly) works OK when I just run the app, i.e. run python src/core
from the root directory.
But when I run pytest
with a test which imports __main__.py
I get
ModuleNotFoundError: No module named 'my_other_file'
on the line in __main__.py
that tries to import "my_other_file". Note that the problem here, in my case, is that, in the course of a pytest test, one application file fails to find another application file in the same package.
USING PYTHONPATH
After a lot of experimentation, and putting an __init__.py
file and a confest.py file in just about every directory I could find (I think the crucial files were __init__.py
added to "tests" and "basic_tests", see above directory structure), and then setting PYTHONPATH to as follows
PYTHONPATH=D:\My Documents\software projects\EclipseWorkspace\my_project\src\core
... I found it worked. Imports in the testing files had to be tweaked a bit, the general pattern being from core import app, project
, but the test files were able to "see" core
, and crucially there was no need to mess around with the import
commands in the app files.
HOWEVER... for some reason the tests now run much slower using this method! Compared to my solution below, where the core
package can be seen to be loaded just once, my suspicion is that the PYTHONPATH solution probably results in vast amounts of code being reloaded again and again. I can't yet confirm this, but I can't see any other explanation.
THE ALTERNATIVE
As I say, I regard this as a flaw in pytest's setup. I have absolutely no idea what pytest does to establish a common-sense setup for sys.path
, but it's obviously getting it wrong. There should be no need to rely on PYTHONPATH, or whatever, and if indeed this is the "official" solution, the documentation on this subject is sorely lacking.
The ONLY working solution, at least on my machines (OS W10 and Linux, as at pytest 7.3.1), is to add the application packages to sys.path
. It is undesirable to mess with sys.path
, but in this case nothing else at all works for me.
The most obvious place to do this is conftest.py
, which gets executed at the start of each pytest run, and which is kept the project root directory. It involves no modification of the app files. Typically (for the above directory/files setup):
this_file_path = pathlib.Path(__file__)
src_core_dir_path_str = str(this_file_path.parent.joinpath('src', 'core'))
sys.path.insert(0, src_core_dir_path_str)
I am currently (2023-08) using pytest v 7.3.1, and have also tried editing pytest.ini, as suggested by ian's 2022 answer, where it is suggested that these configurations in pytest.ini are a new thing to solve the problem since v7 (release 2021-12). In my machines this does not solve the problem: without manually altering sys.path
pytest's attempt to import __main__.py
in my example fails as before: because import my_other_file
raises ModuleNotFoundError: No module named 'my_other_file'
.
PS I have now set up a minimal git repo illustrating the problem: download the .zip at https://github.com/Mrodent/pytest_probs/tree/main, and read what I say in README.md. Interested to know if anyone gets different results to what I get.
--