Recommended approach for pytest>=7: use the pythonpath setting
Recently, pytest has added a new core plugin that supports sys.path modifications via the pythonpath configuration value. The solution is thus much simpler now and doesn’t require any workarounds anymore:
pyproject.toml example:
[tool.pytest.ini_options]
pythonpath = [
"src"
]
pytest.ini example:
[pytest]
pythonpath = src
The path entries are calculated relative to the rootdir, thus the src entry adds path/to/project/src directory to sys.path in this case.
Multiple path entries are also allowed: for a layout
repo/
├── src/
| └── lib.py
├── src2/
| └── lib2.py
└── tests
└── test_lib.py
the configuration
[tool.pytest.ini_options]
pythonpath = [
"src", "src2",
]
or
[pytest]
pythonpath = src src2
will add both lib and lib2 modules to sys.path, so
import lib
import lib2
will both work.
Original answer
Adjusting the PYTHONPATH (as suggested in the comments) is one possibility to solve the import issue. Another is adding an empty conftest.py file in the src directory:
$ touch src/conftest.py
and pytest will add src to sys.path. This is a simple way to trick pytest into adding codebase to sys.path.
However, the src layout is usually selected when you intend to build a distribution, e.g. providing a setup.py with (in this case) explicitly specifying the root package dir:
from setuptools import find_packages, setup
setup(
...
package_dir={'': 'src'},
packages=find_packages(where="src"),
...
)
and installing the package in the development mode (via python setup.py develop or pip install --editable .) while you’re still developing it. This way, your package my_package is correctly integrated in the Python’s site packages structure and there’s no need to fiddle with PYTHONPATH.