lukin0110 / poetry-copier

A copier template for scaffolding Python packages and apps (FastAPI and Gradio) using Poetry as package manager
GNU Affero General Public License v3.0
7 stars 1 forks source link

init-files in tests? #32

Open woutervh opened 8 months ago

woutervh commented 8 months ago

What's up with the init-files under tests?

with an init-file one turns the tests-directory into a python-module which should be avoided, because it changes the behaviour of your tests-discovery.

People usually add an init because they want to import some code from one test-file into another,
which it something one should not do. There are proper solution for re-usable test-code.

lukin0110 commented 8 months ago

I'm often in need of utilities, constants and FactoryBoy factories which I prefer to place in utils.py, constants.py and factories.py. I don't want to mix them up in a conftest, which I purely reserve for fixtures. I never import from other tests and/or confest.py

woutervh commented 8 months ago

In my experience a lot of python devs struggle with what directories should be a module and what not. Many times I see init.py splattered in almost every directory, and weird imports because virtually every directory is a toplevel module.

usually I make 1 toplevel module in src (which should not contain an init.py

src/
   foo/__init__

and a helpers-module for re-usable test-code

tests/
    _helpers/__init__.py

in conftest.py, pytest_plugins will add modules to the PYTHONPATH.

pytest_plugins = [
     "_helpers",
]
woutervh commented 8 months ago

From the moment there is an init-file in the test, tests-discovery changes from a flat namespace to a hierarchical where "tests" is the toplevel module.

pytest then treats it as a module, imports it in a hacky way,
and people are tempted to mix test-code and reusable test-code, because 'it works'. or worse create depencencies between tests. (fixing that in my current project)

The disadvantage of not having an init is you cannot 2 test-files with the same filename in different dirs. But this can be fixed byb changing the import-mode to importlib, which is recommende for new projectd

see https://docs.pytest.org/en/7.1.x/explanation/goodpractices.html#choosing-an-import-mode

so in pyproject.toml

[tool.pytest.ini_options]
addopts = "--import-mode=importlib"

2) re-usable test-code: create a python-module in a subdir of tests Personally I settled on a "_helpers"-module "tests/_helpers/init.py" Then add it as a pytest-plugin, that will automaticallly put it on the PYTHONPATH

pytest_plugins = [
    "_helpers",
]

but changing the import-mode no longer adds the tests-dir to PYTHONPATH, so this import will stop working

solution: https://github.com/pytest-dev/pytest/issues/8964 see also https://github.com/pmeier/pytest-import (addon no longer needed with #8964 above)

[tool.pytest.ini_options]
addopts = "--import-mode=importlib "
cache_dir = "var/cache/pytest"
testpaths = ["tests"]
pythonpath = ["tests"]   # add tests-dir back to PYTHONPATH,

IMHO making imports from one test-file into another test-file not possible, is a feature, not a bug. So I'm currently considering a src-sublevel, for complete symmetry with src:

pythonpath = ["tests/src"]   # add tests/src to PYTHONPATH,

with module tests/src/_helpers/init.py

then only python-modules in src-dirs