Reputation: 15199
I used easy_install to install pytest on a Mac and started writing tests for a project with a file structure likes so:
repo/
|--app.py
|--settings.py
|--models.py
|--tests/
|--test_app.py
Run py.test
while in the repo directory, and everything behaves as you would expect.
But when I try that same thing on either Linux or Windows (both have pytest 2.2.3 on them), it barks whenever it hits its first import of something from my application path. For instance, from app import some_def_in_app
.
Do I need to be editing my PATH to run py.test on these systems?
Upvotes: 436
Views: 433748
Reputation: 11
My set-up
venv
setup in the vs-code.pytest.ini
setup as:[pytest]
pythonpath = .
PYTHONPATH
but was not somehow working in the virtual environment.root
venv
__init__.py
actions
actions.py
__init__.py
tests
test_actions.py
pytest.ini
test_actions.py
I was doing:from actions.actions import ActionFetch
No action module found exception
Solution: I simple ran tests using:
pytest tests --import-mode=append
See https://docs.pytest.org/en/7.1.x/explanation/pythonpath.html#:~:text=%2D%2Dimport%2Dmode%3Dappend
Upvotes: 1
Reputation: 6493
Recently, I faced a situation where the local imports in the src (target code under test) would fail due to import errors.
We all know that the file is simple not found. So my strategy would be to the folder containing the source in sys.path in a fixture.
My folder structure looks like this:
main_folder
--- target_file.py
--- utils.py
--- sub_utils.py
--- tests
--- test_target.py
--- conftest.py
When I run my tests, the import in my target_file (they look like from utils.py import some_function) . Also utils.py imports functions from sub_utils.py. In all, pandemonium.
What worked for me:
In my conftest.py, I use a fixture like this:
@pytest.fixture(scope="module")
def import_target_file():
from pathlib import Path
import sys
sys.path.append(str(Path(__file__).parent.parent))
get_target_file = importlib.import_module("main_folder.target_file")
yield get_target_file
How does my test file looks:
class TestGetAppointments:
def test_valid_event(self, import_target_file):
"""Test to verify correct response when valid event is provided."""
test_event = {
"pathParameters": {
"start": "1",
"end": "1",
}
}
response = import_target_file.some_function(test_event)
Upvotes: 0
Reputation: 72825
If you're trying to run via VSCode's test runner, in addition to setting PYTHONPATH env by whichever method you choose, try in settings.json
:
{
"python.testing.cwd": "${workspaceFolder}",
// or an appropriate path to your cwd
}
Upvotes: 0
Reputation: 31
If nothing works, make sure your test_module.py is listed under the correct src directory.
Sometimes it will give ModuleNotFoundError
not because modules are misplaced or export PYTHONPATH="${PWD}:${PYTHONPATH}"
is not working, its because test_module.py is placed into a wrong directory under the tests folder.
it should be 1-to-1 mapping relation recursively instead of the root folder should be named as "tests" and the name of the file that include test code should starts with "test_", for example,
./nlu_service/models/transformers.py
./tests/models/test_transformers.py
This was my experience.
Upvotes: 0
Reputation: 528
It is a bit of a shame that this is an issue in Python... But just adding this environment variable is the most comfortable way, IMO:
export PYTHONPATH=$PYTHONPATH:.
You can put this line in you .zshrc or .bashrc file.
Upvotes: 10
Reputation: 3616
Since no one has suggested it, you could also pass the path to the tests in your pytest.ini
file:
[pytest]
...
testpaths = repo/tests
See documentation: https://docs.pytest.org/en/6.2.x/customize.html#pytest-ini
Side effect for Visual Studio Code: it should pick up the unit test in the UI.
Upvotes: 1
Reputation: 123
If this pytest error appears not for your own package, but for a Git-installed package in your package's requirements.txt, the solution is to switch to editable installation mode.
For example, suppose your package's requirements.txt had the following line:
git+https://github.com/foo/bar.git
You would instead replace it with the following:
-e git+https://github.com/foo/bar.git#egg=bar
Upvotes: 0
Reputation: 371
Also if you run pytest
within your virtual environment make sure pytest
module is installed within your virtual environment. Activate your virtual environment and run pip install pytest
.
Upvotes: 3
Reputation: 1
As pointed out by Luiz Lezcano Arialdi, the correct solution is to install your package as an editable package.
Since I am using Pipenv, I thought about adding to his answer a step-by-step how to install the current path as an edible with Pipenv, allowing to run pytest without the need of any mangling code or lose files.
You will need to have the following minimal folder structure (documentation):
package/
package/
__init__.py
module.py
tests/
module_test.py
setup.py
setup.py mostly has the following minium code (documentation):
import setuptools
setuptools.setup(name='package', # Change to your package name
packages=setuptools.find_packages())
Then you just need to run pipenv install --dev -e .
and Pipenv will install the current path as an editable package (the --dev flag is optional) (documentation).
Now you should be able to run pytest
without problems.
Upvotes: 0
Reputation: 11
My solution:
Create the conftest.py
file in the test
directory containing:
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + "/relative/path/to/code/")
This will add the folder of interest to the Python interpreter path without modifying every test file, setting environment variable or messing with absolute/relative paths.
Upvotes: 0
Reputation: 347
I had the same problem in Flask.
When I added:
__init__.py
to the tests folder, the problem disappeared :)
Probably the application couldn't recognize folder tests as a module.
Upvotes: 33
Reputation: 816
I was having the same problem when following the Flask tutorial and I found the answer on the official Pytest documentation. It's a little shift from the way I (and I think many others) are used to do things.
You have to create a setup.py
file in your project's root directory with at least the following two lines:
from setuptools import setup, find_packages
setup(name="PACKAGENAME", packages=find_packages())
where PACKAGENAME is your app's name. Then you have to install it with pip:
pip install -e .
The -e
flag tells pip to install the package in editable or "develop" mode. So the next time you run pytest
it should find your app in the standard PYTHONPATH
.
Upvotes: 6
Reputation: 1
Very often the tests were interrupted due to module being unable to be imported.
After research, I found out that the system is looking at the file in the wrong place and we can easily overcome the problem by copying the file, containing the module, in the same folder as stated, in order to be properly imported.
Another solution proposal would be to change the declaration for the import and show MutPy the correct path of the unit. However, due to the fact that multiple units can have this dependency, meaning we need to commit changes also in their declarations, we prefer to simply move the unit to the folder.
Upvotes: -1
Reputation: 2074
Run pytest
itself as a module with:
python -m pytest tests
This happens when the project hierarchy is, for example, package/src package/tests and in tests you import from src. Executing as a module will consider imports as absolute rather than relative to the execution location.
Upvotes: 69
Reputation: 49157
Yes, the source folder is not in Python's path if you cd
to the tests directory.
You have two choices:
Add the path manually to the test files. Something like this:
import sys, os
myPath = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, myPath + '/../')
Run the tests with the env var PYTHONPATH=../
.
Upvotes: 133
Reputation: 66161
pytest>=7
: use the pythonpath
settingRecently, pytest
has added a new core plugin that supports sys.path
modifications via the pythonpath
configuration value. The solution is thus much simpler now and doesn't require any workarounds anymore:
pyproject.toml
example:
[tool.pytest.ini_options]
pythonpath = [
"."
]
pytest.ini
example:
[pytest]
pythonpath = .
The path entries are calculated relative to the rootdir, thus .
adds repo
directory to sys.path
in this case.
Multiple path entries are also allowed: for a layout
repo/
├── src/
| └── lib.py
├── app.py
└── tests
├── test_app.py
└── test_lib.py
the configuration
[tool.pytest.ini_options]
pythonpath = [
".", "src",
]
or
[pytest]
pythonpath = . src
will add both app
and lib
modules to sys.path
, so
import app
import lib
will both work.
pytest<7
only): conftest
solutionThe least invasive solution is adding an empty file named conftest.py
in the repo/
directory:
$ touch repo/conftest.py
That's it. No need to write custom code for mangling the sys.path
or remember to drag PYTHONPATH
along, or placing __init__.py
into dirs where it doesn't belong (using python -m pytest
as suggested in Apteryx's answer is a good solution though!).
The project directory afterwards:
repo
├── conftest.py
├── app.py
├── settings.py
├── models.py
└── tests
└── test_app.py
pytest
looks for the conftest
modules on test collection to gather custom hooks and fixtures, and in order to import the custom objects from them, pytest
adds the parent directory of the conftest.py
to the sys.path
(in this case the repo
directory).
If you have other project structure, place the conftest.py
in the package root dir (the one that contains packages but is not a package itself, so does not contain an __init__.py
), for example:
repo
├── conftest.py
├── spam
│ ├── __init__.py
│ ├── bacon.py
│ └── egg.py
├── eggs
│ ├── __init__.py
│ └── sausage.py
└── tests
├── test_bacon.py
└── test_egg.py
src
layoutAlthough this approach can be used with the src
layout (place conftest.py
in the src
dir):
repo
├── src
│ ├── conftest.py
│ ├── spam
│ │ ├── __init__.py
│ │ ├── bacon.py
│ │ └── egg.py
│ └── eggs
│ ├── __init__.py
│ └── sausage.py
└── tests
├── test_bacon.py
└── test_egg.py
beware that adding src
to PYTHONPATH
mitigates the meaning and benefits of the src
layout! You will end up with testing the code from repository and not the installed package. If you need to do it, maybe you don't need the src
dir at all.
Of course, conftest
modules are not just some files to help the source code discovery; it's where all the project-specific enhancements of the pytest
framework and the customization of your test suite happen. pytest
has a lot of information on conftest
modules scattered throughout their docs; start with conftest.py
: local per-directory plugins
Also, SO has an excellent question on conftest
modules: In py.test, what is the use of conftest.py files?
Upvotes: 500
Reputation: 10069
We have fixed the issue by adding the following environment variable.
PYTHONPATH=${PYTHONPATH}:${PWD}/src:${PWD}/test
Upvotes: 0
Reputation: 99
According to a post on Medium by Dirk Avery (and supported by my personal experience) if you're using a virtual environment for your project then you can't use a system-wide install of pytest; you have to install it in the virtual environment and use that install.
In particular, if you have it installed in both places then simply running the pytest
command won't work because it will be using the system install. As the other answers have described, one simple solution is to run python -m pytest
instead of pytest
; this works because it uses the environment's version of pytest. Alternatively, you can just uninstall the system's version of pytest; after reactivating the virtual environment the pytest
command should work.
Upvotes: 2
Reputation: 3754
I fixed it by removing the top-level __init__.py
in the parent folder of my sources.
Upvotes: 17
Reputation: 2431
I had a similar issue. pytest
did not recognize a module installed in the environment I was working in.
I resolved it by also installing pytest
into the same environment.
Upvotes: 3
Reputation: 2887
I got this error as I used relative imports incorrectly. In the OP example, test_app.py should import functions using e.g.
from repo.app import *
However liberally __init__.py files are scattered around the file structure, this does not work and creates the kind of ImportError seen unless the files and test files are in the same directory.
from app import *
Here's an example of what I had to do with one of my projects:
Here’s my project structure:
microbit/
microbit/activity_indicator/activity_indicator.py
microbit/tests/test_activity_indicator.py
To be able to access activity_indicator.py from test_activity_indicator.py I needed to:
from microbit.activity_indicator.activity_indicator import *
microbit/
microbit/__init__.py
microbit/activity_indicator/__init__.py
microbit/activity_indicator/activity_indicator.py
microbit/tests/__init__.py
microbit/tests/test_activity_indicator.py
Upvotes: 3
Reputation: 228
For me the problem was tests.py
generated by Django along with tests
directory. Removing tests.py
solved the problem.
Upvotes: 2
Reputation: 7330
I started getting weird ConftestImportFailure: ImportError('No module named ...
errors when I had accidentally added __init__.py
file to my src directory (which was not supposed to be a Python package, just a container of all source).
Upvotes: 10
Reputation: 1502
I was getting this error due to something even simpler (you could even say trivial). I hadn't installed the pytest
module. So a simple apt install python-pytest
fixed it for me.
'pytest' would have been listed in setup.py as a test dependency. Make sure you install the test requirements as well.
Upvotes: 1
Reputation: 457
I created this as an answer to your question and my own confusion. I hope it helps. Pay attention to PYTHONPATH in both the py.test command line and in the tox.ini.
https://github.com/jeffmacdonald/pytest_test
Specifically: You have to tell py.test and tox where to find the modules you are including.
With py.test you can do this:
PYTHONPATH=. py.test
And with tox, add this to your tox.ini:
[testenv]
deps= -r{toxinidir}/requirements.txt
commands=py.test
setenv =
PYTHONPATH = {toxinidir}
Upvotes: 24
Reputation: 6352
I'm not sure why py.test does not add the current directory in the PYTHONPATH itself, but here's a workaround (to be executed from the root of your repository):
python -m pytest tests/
It works because Python adds the current directory in the PYTHONPATH for you.
Upvotes: 485
Reputation: 971
You can run with PYTHONPATH in project root
PYTHONPATH=. py.test
Or use pip install as editable import
pip install -e . # install package using setup.py in editable mode
Upvotes: 41
Reputation: 2644
I had the same problem. I fixed it by adding an empty __init__.py
file to my tests
directory.
Upvotes: 160