63

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?

jodá
  • 342
  • 7
  • 25
Sardathrion - against SE abuse
  • 17,269
  • 27
  • 101
  • 156
  • 11
    If you add `__init__.py` to the tests folder also, does it help? – DBedrenko Feb 28 '18 at 11:38
  • 1
    Adding a `__init__.py` in the tests directory did help. [But now this introduces a subtle problem…](https://docs.pytest.org/en/latest/goodpractices.html#test-discovery). It **should** work without an `__init__.py` file. – Sardathrion - against SE abuse Feb 28 '18 at 11:41
  • What problem does it introduce? The article you linked puts `__init__.py` in the `tests` directory and all directories (that include tests) inside it recursively. – DBedrenko Feb 28 '18 at 12:22
  • But now this introduces a subtle problem: in order to load the test modules from the tests directory, pytest prepends the root of the repository to sys.path, which adds the side-effect that now mypkg is also importable. This is problematic if you are using a tool like tox to test your package in a virtual environment, because you want to test the installed version of your package, not the local code from the repository. – Sardathrion - against SE abuse Feb 28 '18 at 12:50
  • 1
    You have to install your package into your virtualenv. At least that's how I get rid of this error every time. – mehdix Feb 28 '18 at 15:41
  • I hit the same error with PyCharm. My error was self-inflicted. I had forgotten to run `poetry add pytest --group dev`. After that my tests came to life. – rustyMagnet Jan 30 '23 at 16:07

2 Answers2

135

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.

hoefling
  • 59,418
  • 12
  • 147
  • 194
  • Your issue is that you have added an empty `contest.py`. It should be named `conftest.py`. I also had a typo in my answer, fixed that. – hoefling Mar 01 '18 at 09:54
  • 3
    No worries! Btw check the answer in the linked question, the suggestion to install the package in editable mode is probably a more viable solution than abusing `pytest` test discovery mechanism. – hoefling Mar 01 '18 at 18:27
  • Yes it sure is. And if I could make this a python package, I would. However, there are issue doing that in that ancient code base… – Sardathrion - against SE abuse Mar 02 '18 at 08:09
  • 3
    I've tried this solution and it doesn't seem to be working anymore... Python 3.7, pytest version 6.2.1. However running `python3.7 -m pytest` instead of just `pytest` works – tzengia Jan 05 '21 at 05:11
  • 1
    @Expanding-Dev adding `conftest.py` continues to work with 6.2.1, you must have another issue with your project layout. If you want, ask a separate question with a [mcve] provided and ping me in the comments here. – hoefling Jan 05 '21 at 08:56
  • 2
    Thanks! Adding `conftest.py` works with pytest 6.2.4. – Tyrion May 15 '21 at 01:35
  • I wonder why `pip -e .` isn't working: I can import my module just fine in an interpreter – kjohnsen May 20 '21 at 20:14
  • 1
    @kjohnsen editable install should work - most probably the module in question is not included in the list of distributed modules. If you want, ask another question with a [mcve] and ping me in the comments here. – hoefling May 20 '21 at 20:27
  • @hoefling Turns out this is a [VS code issue](https://github.com/microsoft/vscode-python/issues/15398#issuecomment-828388565)...I tried running tests on the terminal myself and it worked fine. – kjohnsen May 24 '21 at 16:56
  • I tried the pyproject.toml approach and it still doesn't see the module. – Galen May 16 '23 at 18:22
30

Simply add __init__.py to the tests directory, and to all directories recursively inside it that contain test files.

DBedrenko
  • 4,871
  • 4
  • 38
  • 73
  • But now this introduces a subtle problem: in order to load the test modules from the tests directory, pytest prepends the root of the repository to sys.path, which adds the side-effect that now mypkg is also importable. This is problematic if you are using a tool like tox to test your package in a virtual environment, because you want to test the installed version of your package, not the local code from the repository. – Sardathrion - against SE abuse Feb 28 '18 at 12:50
  • 2
    thats only a problem if testing the installed version matters to you. If you aren't doing anything weird with packaging it won't make a difference most of the time. – avigil Feb 28 '18 at 16:31
  • 10
    I was worried why `pytest` is not working and `python -m pytest` is working. Thanks. – Kiran Kumar Kotari Dec 26 '18 at 06:02
  • @KiranKumarKotari Thank you! This resolved an issue I was having inside a docker. – infiniteloop May 06 '20 at 01:47
  • 2
    python -m does the imports for you and then runs the file as a script, so it resolves relative imports – onesiumus Oct 19 '20 at 05:39
  • 1
    This is not recommended by pytest: https://docs.pytest.org/en/reorganize-docs/goodpractices.html#choosing-a-test-layout-import-rules – kjohnsen May 20 '21 at 20:11