How to solve ImportError with pytest

Question:

There were already questions regarding this topic. Sometimes programmers put some __init__.py at some places, often it is said one should use absolute paths. However, I don’t get it to work here:

How do I import a class from a package so that tests in pytest run and the code can be used?

At the moment I get pytest or the code passing respective running.

My example project structure is

.
├── testingonly
│   ├── cli.py
│   ├── __init__.py
│   └── testingonly.py
└── tests
    ├── __init__.py
    └── test_testingonly.py

__init__.py is in both cases an empty file.

$ cat testingonly/cli.py
"""Console script for testingonly."""
from testingonly import Tester

def main(args=None):
    """Console script for testingonly."""
    te = Tester()
    return 0

main()
$ cat testingonly/testingonly.py
"""Main module."""
class Tester():
    def __init__(self):
        print("Hello")

This gives – as expected:

$ python3 testingonly/cli.py
Hello

Trying to test this, however, fails:

$ pytest
========================================================= test session starts =========================================================
platform linux -- Python 3.7.3, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /home/stefan/Development/testingonly
collected 0 items / 1 error                                                                                                           

=============================================================== ERRORS ================================================================
_____________________________________________ ERROR collecting tests/test_testingonly.py ______________________________________________
ImportError while importing test module '/home/stefan/Development/testingonly/tests/test_testingonly.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.7/importlib/__init__.py:127: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
tests/test_testingonly.py:10: in <module>
    from testingonly import cli
testingonly/cli.py:2: in <module>
    from testingonly import Tester
E   ImportError: cannot import name 'Tester' from 'testingonly' (/home/stefan/Development/testingonly/testingonly/__init__.py)

Renaming testingonly/testingonly.py to testingonly/mytest.py and changing the imports in test_testingonly.py (from testingonly import mytest) and cli.py (from mytest import Tester) gives

$ pytest
========================================================= test session starts =========================================================
platform linux -- Python 3.7.3, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /home/stefan/Development/testingonly
collected 0 items / 1 error                                                                                                           

=============================================================== ERRORS ================================================================
_____________________________________________ ERROR collecting tests/test_testingonly.py ______________________________________________
ImportError while importing test module '/home/stefan/Development/testingonly/tests/test_testingonly.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.7/importlib/__init__.py:127: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
tests/test_testingonly.py:10: in <module>
    from testingonly import cli
testingonly/cli.py:2: in <module>
    from mytest import Tester
E   ModuleNotFoundError: No module named 'mytest'
======================================================= short test summary info =======================================================
ERROR tests/test_testingonly.py
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
========================================================== 1 error in 0.37s ===========================================================
$ python3 testingonly/cli.py
Hello

The other proposed solution with renaming to mytest.py lets the tests pass, but in cli.py using from testingonly.mytest import Tester gives a NameNotFound error.

$ python3 testingonly/cli.py 
Traceback (most recent call last):
  File "testingonly/cli.py", line 2, in <module>
    from testingonly.mytest import Tester
ModuleNotFoundError: No module named 'testingonly'
$ pytest
========================================================= test session starts =========================================================
platform linux -- Python 3.7.3, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /home/stefan/Development/testingonly
collected 1 item                                                                                                                      

tests/test_testingonly.py .                                                                                                     [100%]

========================================================== 1 passed in 0.12s ==========================================================
Asked By: Stefan Bollmann

||

Answers:

The self-named module testingonly and file name of testingonly.py may be causing some issues with the way the modules are imported.

Remove the __init__.py from the tests directory. Ref this answer .

Try renaming testingonly.py to mytest.py and then importing it into your project again.

In the cli.py, it should be:

from testingonly.mytest import Tester

And then for your test file test_testingonly.py:

from testingonly.mytest import Tester

You’re test_testingonly.py file should look like this:

import pytest
from testingonly.mytest import Tester  # Import the Tester Class


def test_tester(capsys):
    # Create the Tester Class
    te = Tester()
    # Get the captured output
    captured = capsys.readouterr()
    # Assert that the capture output is tested
    assert captured.out == "Hellon"

Finally, Run your tests with:

python -m pytest tests/

Here is a fully working example based off of your code: https://github.com/cdesch/testingonly

Answered By: DogEatDog

This is only a python path issue. Your pytest is unable to find the path to source modules.

One way to fix this: export PYTHONPATH=$PYTHONPATH:. before running pytest. Here you are saying that your current folder represented by a dot is a python module path. This is what happens when you also run via python -m pytest, where the module path gets set automatically.

But this requires you to set the PYTHONPATH every time before you run pytest, or use a long command for pytest which is very inconvenient.

One earlier method to overcome this issue: install pytest-pythonpath plugin to automatically add PYTHONPATH to environment. This module is obsolete now (as seen in its home page) because pytest.ini allows you to specify python path directly from v7 onwards

So now all you have to do is this: ensure that your pytest is version 7 or above, and your pytest.ini file should contain these lines:

[pytest]
testpaths = tests
python_files = tests.py test_*.py
pythonpath = .

Now your pytest will have no trouble finding your source modules. You may also add your source folders to pythonpath in pytest.ini if required

pytest documentation for pythonpath: https://docs.pytest.org/en/7.0.x/reference/reference.html#confval-pythonpath

Answered By: Mani
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.