Search code examples
pythonpytestpython-import

How to solve ImportError with pytest


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 ==========================================================

Solution

  • 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 == "Hello\n"
    

    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