2

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 ==========================================================
Stefan Bollmann
  • 558
  • 2
  • 10
  • 25
  • This answer on relative Python Imports may help as well https://stackoverflow.com/questions/2183205/importing-a-module-in-nested-packages – DogEatDog Jan 03 '22 at 19:03
  • 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. – hoefling Jan 04 '22 at 16:41
  • 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'](https://stackoverflow.com/q/10253826/2650249) for more details. – hoefling Jan 04 '22 at 16:43
  • Ah, and the correct replacement for `from testingonly import Tester` is of course `from testingonly.testingonly import Tester` – hoefling Jan 04 '22 at 16:44

1 Answers1

1

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

DogEatDog
  • 2,552
  • 1
  • 33
  • 61