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.

Answered By: DBedrenko

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.

Answered By: hoefling
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.