I started a project with what I would describe as a "standard" Python project structure, whereby my code is organized into a set of packages which all live in the top level directory. In addition to this, there is a tests
directory which contains .py
files with functions which define the test cases.
I would now like to segregate this from another new project which I will add to the same repository.
To do this, I propose to create two top level directories new_project
and old_project
under which everything will be organized. (Given the names, you could imagine this is part of some kind of migration.)
I don’t know how to make this work with pytest as it integrates with VS Code.
Here’s the proposed project structure, as a MWE:
new_project/
simple_package/
__init__.py
tests/
test_simple_package.py
old_project/
# copy & paste of the above
The files can contain minimal code to demonstrate the point.
$ cat new_project/simple_package/__init__.py
a = 1
$ cat new_project/tests/test_simple_package.py
from simple_package import a
def test_a():
assert a == 1
I see the following error message:
- In the
pytest
Test Explorer sidebar (left hand side, beaker icon): pytest Discovery Error [pytest-subdirectory-test] Show output to view error logs - In the output:
E ModuleNotFoundError: No module named 'simple_package'
Is it possible to configure pytest
to work with such a repository structure?
It would seem strange that this wouldn’t work, because many projects would likely be a mix of different code written in different programming languages. Therefore it would seem strange if the VS Code pytest
integration can only be made to work in the most simple case where all the Python packages are at the top level of the repository.
2
Answers
There is (what I personally feel is) a "hack" to make this work. It involves modifying the
PYTHONPATH
, from within code, from within the test source files.Here's why I don't like this:
PYTHONPATH
is a good solution to any problemtest_x.py
test file needs to have this same hack as the first few linessys.path
There is another solution, which again, is a bit of a hack, and again, I don’t really like it much. It involves making everything into a single package.
Here’s how we do that:
Add
__init__.py
tonew_project
andold_project
. This converts both directorys into Python Packages. Because these packages exist at the top level, we can import from them without having to modifyPYTHONPATH
.The code in the tests needs to change slightly. The import statement becomes:
Unlike my previous solution (hack), this has the advantage that VS Code/Python plugin can automatically update the imports if we move files and folders around, or rename things. (Under some, but not all conditions.)
However I still don’t like this. In particular, there are some potential problems:
In addition, I don’t this this really solves the problem. I think we need to move the test folders to the top level as well (not 100% sure on this)
If so we end up with this structure:
This maybe isn’t the end of the world, since the tests are still separated (from the source). But the fact that they are bundled together in the same subdirectory is potentially an issue because we can’t split the project into two independent parts, whereas if the two top level directories were
new_project
andold_project
these would truly be two independent source trees which we could split into two independent repositories.You may ask: Since I propose to make both
new_project
andold_project
into Packages, why bother with these at all. Just keep all the previously existing packages at the top level.The issue with this is for a complex project with many packages, there probably won’t be an obvious way to tell which packages belong to which project.
Consider this example:
Which of these are part of the new project and which belong to the old project? There is no way to tell.