pytest cannot find module
Question:
I am following the pytest good practices or at least I think I am. However, pytest cannot find my module. It seems not to include the current directory in its PYTHONPATH
.
The source file:
def add(x, y):
return x + y
The test file:
import pytest
from junk.ook import add
def test_add_true():
assert add(1, 1) == 2
And the shell output with a Python 3 virtual environment called "p3".
p3; pwd
/home/usr/tmp/junk
p3; ls
total 0
0 junk/ 0 tests/
p3; ls junk
total 4.0K
4.0K ook.py 0 __init__.py
p3; ls tests
total 4.0K
4.0K test_ook.py 0 __pycache__/
p3; pytest
============================= test session starts ==============================
platform linux -- Python 3.4.5, pytest-3.4.1, py-1.5.2, pluggy-0.6.0
rootdir: /home/usr/tmp/junk, inifile:
collected 0 items / 1 errors
==================================== ERRORS ====================================
______________________ ERROR collecting tests/test_ook.py ______________________
ImportError while importing test module '/home/usr/tmp/junk/tests/test_ook.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
tests/test_ook.py:2: in <module>
from junk.ook import add
E ImportError: No module named 'junk'
!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!!
=========================== 1 error in 0.08 seconds ============================
def test_add_true():
assert add(1, 1) == 2
However, running the following does work fine.
p3; python -m pytest tests/
============================= test session starts ==============================
platform linux -- Python 3.4.5, pytest-3.4.1, py-1.5.2, pluggy-0.6.0
rootdir: /home/usr/tmp/junk, inifile:
collected 1 item
tests/test_ook.py . [100%]
=========================== 1 passed in 0.02 seconds ===========================
What am I doing wrong?
Answers:
Simply add __init__.py
to the tests
directory, and to all directories recursively inside it that contain test files.
Update for pytest 7 and newer: use the pythonpath
setting
Recently, 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 junk
directory to sys.path
in this case.
Multiple path entries are also allowed: for a layout
junk/
├── src/
| └── lib.py
├── junk/
│ ├── __init__.py
│ └── ook.py
└── tests
├── test_app.py
└── test_lib.py
the configuration
[tool.pytest.ini_options]
pythonpath = [
".", "src",
]
or
[pytest]
pythonpath = . src
will add both lib
module and junk
package to sys.path
, so
import junk
import lib
will both work.
Original answer
Just put an empty conftest.py
file in the project root directory:
$ pwd
/home/usr/tmp/junk
$ touch conftest.py
Your project structure should become:
junk
├── conftest.py
├── junk
│ ├── __init__.py
│ └── ook.py
└── tests
└── test_ook.py
What happens here: when pytest
discovers a conftest.py
, it modifies sys.path
so it can import stuff from the conftest module. So, since now an empty conftest.py
is found in rootdir, pytest
will be forced to append it to sys.path
. The side effect of this is that your junk
module becomes importable.
I am following the pytest good practices or at least I think I am. However, pytest cannot find my module. It seems not to include the current directory in its PYTHONPATH
.
The source file:
def add(x, y):
return x + y
The test file:
import pytest
from junk.ook import add
def test_add_true():
assert add(1, 1) == 2
And the shell output with a Python 3 virtual environment called "p3".
p3; pwd
/home/usr/tmp/junk
p3; ls
total 0
0 junk/ 0 tests/
p3; ls junk
total 4.0K
4.0K ook.py 0 __init__.py
p3; ls tests
total 4.0K
4.0K test_ook.py 0 __pycache__/
p3; pytest
============================= test session starts ==============================
platform linux -- Python 3.4.5, pytest-3.4.1, py-1.5.2, pluggy-0.6.0
rootdir: /home/usr/tmp/junk, inifile:
collected 0 items / 1 errors
==================================== ERRORS ====================================
______________________ ERROR collecting tests/test_ook.py ______________________
ImportError while importing test module '/home/usr/tmp/junk/tests/test_ook.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
tests/test_ook.py:2: in <module>
from junk.ook import add
E ImportError: No module named 'junk'
!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!!
=========================== 1 error in 0.08 seconds ============================
def test_add_true():
assert add(1, 1) == 2
However, running the following does work fine.
p3; python -m pytest tests/
============================= test session starts ==============================
platform linux -- Python 3.4.5, pytest-3.4.1, py-1.5.2, pluggy-0.6.0
rootdir: /home/usr/tmp/junk, inifile:
collected 1 item
tests/test_ook.py . [100%]
=========================== 1 passed in 0.02 seconds ===========================
What am I doing wrong?
Simply add __init__.py
to the tests
directory, and to all directories recursively inside it that contain test files.
Update for pytest 7 and newer: use the pythonpath
setting
Recently, 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 junk
directory to sys.path
in this case.
Multiple path entries are also allowed: for a layout
junk/
├── src/
| └── lib.py
├── junk/
│ ├── __init__.py
│ └── ook.py
└── tests
├── test_app.py
└── test_lib.py
the configuration
[tool.pytest.ini_options]
pythonpath = [
".", "src",
]
or
[pytest]
pythonpath = . src
will add both lib
module and junk
package to sys.path
, so
import junk
import lib
will both work.
Original answer
Just put an empty conftest.py
file in the project root directory:
$ pwd
/home/usr/tmp/junk
$ touch conftest.py
Your project structure should become:
junk
├── conftest.py
├── junk
│ ├── __init__.py
│ └── ook.py
└── tests
└── test_ook.py
What happens here: when pytest
discovers a conftest.py
, it modifies sys.path
so it can import stuff from the conftest module. So, since now an empty conftest.py
is found in rootdir, pytest
will be forced to append it to sys.path
. The side effect of this is that your junk
module becomes importable.