9

I am storing all the pytest files in the tests sub-directory under the project root. There is no __init__.py in that directory or above and pytest tests/test_a.py works as expected. I can also run pytest test_a.py directly from within the tests folder.

|--<project_root>
+---[tests]
    test_a.py
    base.py
    config.py
    conftest.py

The test class in test_a.py inherits from base.py. Now, the problem is that because of the missing __init__.py the IDE tooling does not work and the imports cannot be resolved. Adding __init__.py under the tests resolves all import errors in the IDE, but tests won't run anymore with pytest test_a.py because py.test fails to import conftest.py.

In my conftest.py I have the following imports:

from config import HOST_URL_DEFAULT, USER, PASSWORD

which results in:

ModuleNotFoundError: No module named 'config'
ERROR: could not load /project_root/tests/conftest.py

Is there a way to solve this so that both IDE tooling works and pytest keeps working? I would like to avoid using dot imports, if possible.

Update: After reading this answer I think I am finally starting to understand a bit more how python imports work.

ccpizza
  • 28,968
  • 18
  • 162
  • 169
  • Why does ``pytest`` fail to import ``config.py``? – MisterMiyagi Jun 11 '18 at 11:26
  • @MisterMiyagi: it was `conftest.py` that pytest cannot import, sorry for the typo. I am not explicitly importing `conftest.py` anywhere in my test code, but the file is present and I am using it. I updated the question to show what exactly is causing the error. – ccpizza Jun 11 '18 at 11:35
  • So the issue is rather with your IDE than with `pytest`? Maybe it's possible to tell the IDE that `tests` is a namespace package somewhere in the project settings. If you place `__init__.py` in the `tests` dir, the qualified name of `config` module will change to `tests.config` and you have to adapt the imports as the answer suggests. One way or the other, it may be worth checking out [Good Integration Practices](https://docs.pytest.org/en/latest/goodpractices.html#tests-outside-application-code) for hints on different test layouts. – hoefling Jun 11 '18 at 12:22
  • 2
    At least in PyCharm, you can add `tests` as source root in the project settings; this resolves the import issues. – hoefling Jun 11 '18 at 12:24

1 Answers1

10

Add __init__.py and use a relative or absolute import in conftest.py:

# relative
from .config import HOST_URL_DEFAULT, USER, PASSWORD
# absolute
from tests.config import HOST_URL_DEFAULT, USER, PASSWORD

In a package (identified by __init__.py), you need unambiguous import paths. Using unqualified imports (from config import ...) depends on PYTHONPATH or sys.modules including your package root - this is generally not robust and should be avoided as it circumvents the package infrastructure.

What you have here (config.py used by other modules) is a package. Treat it as one.


pytest does not discourage the use of a test package with an __init__.py! There are veritable use cases for this - such as yours.

What pytest does discourage is having a source package and test package in the same source root. However, that means you should move your source package, not your test package!

mysource/  # root directory for both mypackage and mytests
+- mypackage/  # should be src/mypackage instead!
   +- __init__.py
   +- abcd.py
+- mytests
   +- __init__.py
   +- tests_abcd.py  # ``from mypackage import abcd`` imports from source

The problem is that running mytests means that mypackage is importable from source as well. That is, your tests are working on your source package, not your installed package. If there are any errors in your installation procedure, testing will not catch it. See the blog linked in the pytest docs for details.

MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
  • My understanding was that `pytest` discourages the use of `__init__.py` for the tests, which does somehow manage to run the code without turning everything into a package. – ccpizza Jun 11 '18 at 11:41
  • ``pytest`` does not require you to turn non-packages into packages. You *already have* a package there, though. – MisterMiyagi Jun 11 '18 at 11:44
  • @ccpizza I have added some further description that ``pytest`` discourage *having a source package next to* a test package, not having a test package per se. – MisterMiyagi Jun 11 '18 at 12:04