How to solve ImportError with pytest
Asked Answered
B

3

9

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 ==========================================================
Bantam answered 3/1, 2022 at 18:49 Comment(4)
This answer on relative Python Imports may help as well #2183705Wither
First of all, from testingonly import Tester is wrong. The Tester class is defined in module with qualname testingonly.testingonly as is not exported in testingonly/__init__.py, so the import will not resolve. It works in testingonly.cli only because it is on the same level as testingonly.testingonly, with a relative import as fallback. To verify, create some foo.py in the root dir with a single line from testingonly import Tester and run python foo.py - you will get the same error.Animality
Second, pytest doesn't add the project root dir to sys.path by default - you should take care of it yourself. Either run python -m pytest, or add an empty file named conftest.py to the project root dir. Check out the answers in PATH issue with pytest 'ImportError: No module named YadaYadaYada' for more details.Animality
Ah, and the correct replacement for from testingonly import Tester is of course from testingonly.testingonly import TesterAnimality
W
2

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

Wither answered 3/1, 2022 at 19:1 Comment(8)
I updated my question with the from mytest import Tester try. The other one gives an error with pytest. To be added soon...Bantam
Did you get it to work? I saw you accepted the answer.Wither
Yes, it did the trick! Thank you.Bantam
Sry, it did not do the trick. This always happens to me at the moment, I get confused with what I tested an what not...rewrite the question.Bantam
If you can post the sample code on github, I can take a look.Wither
I posted a sample on github that works with your Code. I also added a MyCalc class into the project to show another class being tested. Here is the code: github.com/cdesch/testingonlyWither
Let us continue this discussion in chat.Bantam
I discovered that using pytest . failed but using python -m pytest . worksLauer
M
4

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

Moonlight answered 17/3, 2023 at 13:33 Comment(1)
Update your PYTHONPATH in the venv activate script. I presume this is what IDEs always do anyway. It seems IDEs can do whatever crap they like and no-one calls it a hack, and then they say something like "ooh have you tried PyCharm it's so cool". Then they start complaining about "works for me" inconsistency problems.Morganica
W
2

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

Wither answered 3/1, 2022 at 19:1 Comment(8)
I updated my question with the from mytest import Tester try. The other one gives an error with pytest. To be added soon...Bantam
Did you get it to work? I saw you accepted the answer.Wither
Yes, it did the trick! Thank you.Bantam
Sry, it did not do the trick. This always happens to me at the moment, I get confused with what I tested an what not...rewrite the question.Bantam
If you can post the sample code on github, I can take a look.Wither
I posted a sample on github that works with your Code. I also added a MyCalc class into the project to show another class being tested. Here is the code: github.com/cdesch/testingonlyWither
Let us continue this discussion in chat.Bantam
I discovered that using pytest . failed but using python -m pytest . worksLauer
D
0

Good day, guys

I faced the same problem and, after read a lot of options to solve it, I decided to download the Python installer with my current versions installed (3.10.9 and 3.12.1), run it, and choose Repair.

The problem is solved.

Doubler answered 4/9, 2024 at 17:4 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Rules

© 2022 - 2025 — McMap. All rights reserved.