Skip to content

Commit 75ec893

Browse files
authored
Merge pull request #2297 from nicoddemus/init-files-docs
Attempt to clarify the confusion regarding __init__ files and unique test names
2 parents 76df774 + 272aba9 commit 75ec893

File tree

2 files changed

+87
-46
lines changed

2 files changed

+87
-46
lines changed

doc/en/goodpractices.rst

Lines changed: 85 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -30,68 +30,106 @@ Within Python modules, ``pytest`` also discovers tests using the standard
3030

3131

3232
Choosing a test layout / import rules
33-
------------------------------------------
33+
-------------------------------------
3434

3535
``pytest`` supports two common test layouts:
3636

37-
* putting tests into an extra directory outside your actual application
38-
code, useful if you have many functional tests or for other reasons
39-
want to keep tests separate from actual application code (often a good
40-
idea)::
37+
Tests outside application code
38+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4139

42-
setup.py # your setuptools Python package metadata
40+
Putting tests into an extra directory outside your actual application code
41+
might be useful if you have many functional tests or for other reasons want
42+
to keep tests separate from actual application code (often a good idea)::
43+
44+
setup.py
4345
mypkg/
4446
__init__.py
45-
appmodule.py
47+
app.py
48+
view.py
4649
tests/
4750
test_app.py
51+
test_view.py
4852
...
4953

54+
This way your tests can run easily against an installed version
55+
of ``mypkg``.
56+
57+
Note that using this scheme your test files must have **unique names**, because
58+
``pytest`` will import them as *top-level* modules since there are no packages
59+
to derive a full package name from. In other words, the test files in the example above will
60+
be imported as ``test_app`` and ``test_view`` top-level modules by adding ``tests/`` to
61+
``sys.path``.
5062

51-
* inlining test directories into your application package, useful if you
52-
have direct relation between (unit-)test and application modules and
53-
want to distribute your tests along with your application::
63+
If you need to have test modules with the same name, you might add ``__init__.py`` files to your
64+
``tests`` folder and subfolders, changing them to packages::
5465

55-
setup.py # your setuptools Python package metadata
66+
setup.py
5667
mypkg/
57-
__init__.py
58-
appmodule.py
5968
...
60-
test/
61-
test_app.py
62-
...
69+
tests/
70+
__init__.py
71+
foo/
72+
__init__.py
73+
test_view.py
74+
bar/
75+
__init__.py
76+
test_view.py
77+
78+
Now pytest will load the modules as ``tests.foo.test_view`` and ``tests.bar.test_view``, allowing
79+
you to have modules with the same name. But now this introduces a subtle problem: in order to load
80+
the test modules from the ``tests`` directory, pytest prepends the root of the repository to
81+
``sys.path``, which adds the side-effect that now ``mypkg`` is also importable.
82+
This is problematic if you are using a tool like `tox`_ to test your package in a virtual environment,
83+
because you want to test the *installed* version of your package, not the local code from the repository.
84+
85+
In this situation, it is **strongly** suggested to use a ``src`` layout where application root package resides in a
86+
sub-directory of your root::
87+
88+
setup.py
89+
src/
90+
mypkg/
91+
__init__.py
92+
app.py
93+
view.py
94+
tests/
95+
__init__.py
96+
foo/
97+
__init__.py
98+
test_view.py
99+
bar/
100+
__init__.py
101+
test_view.py
63102

64-
Important notes relating to both schemes:
65103

66-
- **make sure that "mypkg" is importable**, for example by typing once::
104+
This layout prevents a lot of common pitfalls and has many benefits, which are better explained in this excellent
105+
`blog post by Ionel Cristian Mărieș <https://blog.ionelmc.ro/2014/05/25/python-packaging/#the-structure>`_.
67106

68-
pip install -e . # install package using setup.py in editable mode
107+
Tests as part of application code
108+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
69109

70-
- **avoid "__init__.py" files in your test directories**.
71-
This way your tests can run easily against an installed version
72-
of ``mypkg``, independently from the installed package if it contains
73-
the tests or not.
110+
Inlining test directories into your application package
111+
is useful if you have direct relation between tests and application modules and
112+
want to distribute them along with your application::
74113

75-
- With inlined tests you might put ``__init__.py`` into test
76-
directories and make them installable as part of your application.
77-
Using the ``pytest --pyargs mypkg`` invocation pytest will
78-
discover where mypkg is installed and collect tests from there.
79-
With the "external" test you can still distribute tests but they
80-
will not be installed or become importable.
114+
setup.py
115+
mypkg/
116+
__init__.py
117+
app.py
118+
view.py
119+
test/
120+
__init__.py
121+
test_app.py
122+
test_view.py
123+
...
124+
125+
In this scheme, it is easy to your run tests using the ``--pyargs`` option::
81126

82-
Typically you can run tests by pointing to test directories or modules::
127+
pytest --pyargs mypkg
83128

84-
pytest tests/test_app.py # for external test dirs
85-
pytest mypkg/test/test_app.py # for inlined test dirs
86-
pytest mypkg # run tests in all below test directories
87-
pytest # run all tests below current dir
88-
...
129+
``pytest`` will discover where ``mypkg`` is installed and collect tests from there.
130+
131+
Note that this layout also works in conjunction with the ``src`` layout mentioned in the previous section.
89132

90-
Because of the above ``editable install`` mode you can change your
91-
source code (both tests and the app) and rerun tests at will.
92-
Once you are done with your work, you can `use tox`_ to make sure
93-
that the package is really correct and tests pass in all
94-
required configurations.
95133

96134
.. note::
97135

@@ -144,7 +182,13 @@ for installing your application and any dependencies
144182
as well as the ``pytest`` package itself. This ensures your code and
145183
dependencies are isolated from the system Python installation.
146184

147-
If you frequently release code and want to make sure that your actual
185+
You can then install your package in "editable" mode::
186+
187+
pip install -e .
188+
189+
which lets you change your source code (both tests and application) and rerun tests at will.
190+
191+
Once you are done with your work and want to make sure that your actual
148192
package passes all tests you may want to look into `tox`_, the
149193
virtualenv test automation tool and its `pytest support
150194
<https://tox.readthedocs.io/en/latest/example/pytest.html>`_.
@@ -154,11 +198,6 @@ options. It will run tests against the installed package and not
154198
against your source code checkout, helping to detect packaging
155199
glitches.
156200

157-
Continuous integration services such as Jenkins_ can make use of the
158-
``--junitxml=PATH`` option to create a JUnitXML file and generate reports (e.g.
159-
by publishing the results in a nice format with the `Jenkins xUnit Plugin
160-
<https://wiki.jenkins-ci.org/display/JENKINS/xUnit+Plugin>`_).
161-
162201

163202
Integrating with setuptools / ``python setup.py test`` / ``pytest-runner``
164203
--------------------------------------------------------------------------

tox.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ commands=
106106
pytest -ra {posargs:testing/test_unittest.py}
107107

108108
[testenv:docs]
109+
skipsdist=True
110+
usedevelop=True
109111
basepython=python
110112
changedir=doc/en
111113
deps=

0 commit comments

Comments
 (0)