Something that I couldn't figure out was how to import my app so that I could test it with pytests. Isolated tests would work, but anytime I tried to import something from my app like so
from app.main import app from fastapi.testclient import TestClient client = TestClient(app) def test_root(): response = client.get("/") assert response.status_code == 200
it would fail with
__________________________________________________________________________________________ ERROR collecting tests/main_test.py __________________________________________________________________________________________ ImportError while importing test module '/some/project/path/tests/main_test.py'. Hint: make sure your test modules/packages have valid Python names. Traceback: ../../.asdf/installs/python/3.12.0/lib/python3.12/importlib/__init__.py:90: in import_module return _bootstrap._gcd_import(name[level:], package, level) tests/main_test.py:1: in <module> from app.main import app E ModuleNotFoundError: No module named 'app'
The problem is that even though you might be running pytest in the root directory of your app, it doesn’t know it needs to look there. This is fixed with setting the pythonpath
. One option is to set it via environment variables, but another option for modern pytest instances (at least v7.4) is to set this as a project config either through pytest.ini, pyproject.toml or one of the other configuration options. Since discovering uv I recommend the toml approach.
No matter how it’s done. all that’s needed is the following (using the toml approach):
[tool.pytest.ini_options]
pythonpath = "."
It makes me wonder why pytest doesn’t set this as the default.
python -m pytest
Another option is to use python -m pytest
. I realized this after seeing this answer. What this does is that it adds the current working directory to sys.path and that will allow this to work.
It turns out this is "documented". In fact, it's documented in another place as well, but I feel that this should be more obvious. I understand that the argument can be made that this should be known, I'd respond that this isn't as obvious as folks might think.
Back