rST parser warns about empty footnotes and citations.
[docutils.git] / docutils / docs / dev / testing.txt
blobb1382e5f457d2f5649f3e66589235136b33cdc73
1 .. include:: ../header.txt
3 ===================
4  Docutils_ Testing
5 ===================
7 :Authors: Lea Wiemann <LeWiemann@gmail.com>;
8           David Goodger <goodger@python.org>;
9           Docutils developers <docutils-developers@lists.sourceforge.net>
10 :Revision: $Revision$
11 :Date: $Date$
12 :Copyright: This document has been placed in the public domain.
14 :Abstract: This document describes how to run the Docutils test suite,
15            how the tests are organized and how to add new tests or modify
16            existing tests.
18 .. _Docutils: https://docutils.sourceforge.io/
20 .. contents::
22 When adding new functionality (or fixing bugs), be sure to add test
23 cases to the test suite.  Practise test-first programming; it's fun,
24 it's addictive, and it works!
27 Running the Test Suite
28 ======================
30 Before checking in any changes, run the entire Docutils test suite to
31 be sure that you haven't broken anything.  From a shell do::
33     cd docutils/test
34     python -u alltests.py
36 Before `checking in`__ changes to the Docutils core, run the tests on
37 all `supported Python versions`_ (see below for details).
38 In a pinch, the edge cases should cover most of it.
40 .. note::
41    The ``alltests.py`` test runner is based on the standard library's unittest_
42    framework.
43    Since Docutils 0.19, running ``python -m unittest`` and the pytest_
44    framework no longer result in spurious failures (cf. `bug #270`_).
45    However, there are differences in the reported number of tests
46    (``alltests.py`` also counts sub-tests).
47    In future, running the test suite may require pytest_.
49 __ policies.html#check-ins
50 .. _unittest: https://docs.python.org/3/library/unittest.html
51 .. _pytest: https://pypi.org/project/pytest
52 .. _`bug #270`: https://sourceforge.net/p/docutils/bugs/270/
55 .. _Python versions:
57 Testing across multiple Python versions
58 ---------------------------------------
60 A Docutils release has a commitment to support a minimum Python version
61 and beyond (see dependencies__ in README.txt). Before a release is cut,
62 tests must pass in all `supported versions`_. [#]_
64 __ ../../README.html#dependencies
66 You can use `tox`_ to test with all supported versions in one go.
67 From the shell::
69     cd docutils
70     tox
72 To test a specific version, use the ``pyNN`` environment. For example::
74     tox -e py37
76 `pyenv`_ can be installed and configured (see `installing pyenv`_) to
77 get multiple Python versions::
79     # assuming your system runs 3.9.x
80     pyenv install 3.7.12
81     pyenv install 3.8.12
82     pyenv install 3.10.1
83     pyenv global system 3.7.12 3.8.12 3.10.1 3.11.0
85     # reset your shims
86     rm -rf ~/.pyenv/shims && pyenv rehash
88 This will give you ``python3.7`` through ``python3.11``.
89 Then run::
91     python3.7 -u alltests.py
92     [...]
93     python3.11 -u alltests.py
95 .. note::
96    When using the `Python launcher for Windows`__, specify the Python version
97    as option, e.g., ``py -3.9 -u alltests.py`` for Python 3.9.
99    .. cf. https://sourceforge.net/p/docutils/bugs/434/
100    __ https://docs.python.org/3/using/windows.html#python-launcher-for-windows
103 .. [#] Good resources covering the differences between Python versions
104    are the `What's New` documents (`What's New in Python 3.11`__ and
105    similar).
107    __ https://docs.python.org/3/whatsnew/3.11.html
110 .. _supported versions:
111 .. _supported Python versions: ../../README.html#requirements
112 .. _pyenv: https://github.com/yyuu/pyenv
113 .. _installing pyenv: https://github.com/yyuu/pyenv#installation
114 .. _tox: https://pypi.org/project/tox/
117 Unit Tests
118 ==========
120 Unit tests test single functions or modules (i.e. whitebox testing).
122 If you are implementing a new feature, be sure to write a test case
123 covering its functionality.  It happens very frequently that your
124 implementation (or even only a part of it) doesn't work with an older
125 (or even newer) Python version, and the only reliable way to detect
126 those cases is using tests.
128 Often, it's easier to write the test first and then implement the
129 functionality required to make the test pass.
132 Writing New Tests
133 -----------------
135 When writing new tests, it very often helps to see how a similar test
136 is implemented.  For example, the files in the
137 ``test_parsers/test_rst/`` directory all look very similar.  So when
138 adding a test, you don't have to reinvent the wheel.
140 If there is no similar test, you can write a new test from scratch
141 using Python's ``unittest`` module.  For an example, please have a
142 look at the following imaginary ``test_square.py``::
144     #! /usr/bin/env python3
146     # $Id$
147     # Author: Your Name <your_email_address@example.org>
148     # Copyright: This module has been placed in the public domain.
150     """
151     Test module for docutils.square.
152     """
154     import unittest
155     if __name__ == '__main__':
156         # prepend the "docutils root" to the Python library path
157         # so we import the local `docutils` package.
158         sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
160     import docutils.square
163     class SquareTest(unittest.TestCase):
165         def test_square(self):
166             self.assertEqual(docutils.square.square(0), 0)
167             self.assertEqual(docutils.square.square(5), 25)
168             self.assertEqual(docutils.square.square(7), 49)
170         def test_square_root(self):
171             self.assertEqual(docutils.square.sqrt(49), 7)
172             self.assertEqual(docutils.square.sqrt(0), 0)
173             self.assertRaises(docutils.square.SquareRootError,
174                               docutils.square.sqrt, 20)
177     if __name__ == '__main__':
178         unittest.main()
180 For more details on how to write tests, please refer to the
181 documentation of the ``unittest`` module.
183 .. Note::
185    Unit tests and functional test should generally disable configuration
186    files, setting the `_disable_config`__ setting to True ::
188      settings_overrides['_disable_config'] = True
190    in order to be independent on the users local configuration.
192    __ ../user/config.html#disable-config
194 .. _functional:
196 Functional Tests
197 ================
199 The directory ``test/functional/`` contains data for functional tests.
201 Performing functional testing means testing the Docutils system as a
202 whole (i.e. blackbox testing).
205 Directory Structure
206 -------------------
208 + ``functional/`` The main data directory.
210   + ``input/`` The input files.
212     - ``some_test.txt``, for example.
214   + ``output/`` The actual output.
216     - ``some_test.html``, for example.
218   + ``expected/`` The expected output.
220     - ``some_test.html``, for example.
222   + ``tests/`` The config files for processing the input files.
224     - ``some_test.py``, for example.
227 The Testing Process
228 -------------------
230 When running ``test_functional.py``, all config files in
231 ``functional/tests/`` are processed.  (Config files whose names begin
232 with an underscore are ignored.)  The current working directory is
233 always Docutils' main test directory (``test/``).
235 For example, ``functional/tests/some_test.py`` could read like this::
237     # Source and destination file names.
238     test_source = "some_test.txt"
239     test_destination = "some_test.html"
241     # Keyword parameters passed to publish_file.
242     reader_name = "standalone"
243     parser_name = "rst"
244     writer_name = "html"
245     settings_overrides['output-encoding'] = 'utf-8'
246     # Relative to main ``test/`` directory.
247     settings_overrides['stylesheet_path'] = '../docutils/writers/html4css1/html4css1.css'
249 The two variables ``test_source`` and ``test_destination`` contain the
250 input file name (relative to ``functional/input/``) and the output
251 file name (relative to ``functional/output/`` and
252 ``functional/expected/``).  Note that the file names can be chosen
253 arbitrarily.  However, the file names in ``functional/output/`` *must*
254 match the file names in ``functional/expected/``.
256 ``test_source`` and ``test_destination`` are removed from the
257 namespace, as are all variables whose names begin with an underscore
258 ("_").  The remaining names are passed as keyword arguments to
259 ``docutils.core.publish_file``, so you can set reader, parser, writer
260 and anything else you want to configure.  Note that
261 ``settings_overrides`` is already initialized as a dictionary *before*
262 the execution of the config file.
265 Creating New Tests
266 ------------------
268 In order to create a new test, put the input test file into
269 ``functional/input/``.  Then create a config file in
270 ``functional/tests/`` which sets at least input and output file names,
271 reader, parser and writer.
273 Now run ``test_functional.py``.  The test will fail, of course,
274 because you do not have an expected output yet.  However, an output
275 file will have been generated in ``functional/output/``.  Check this
276 output file for validity [#]_ and correctness.  Then copy the file to
277 ``functional/expected/``.
279 If you rerun ``test_functional.py`` now, it should pass.
281 If you run ``test_functional.py`` later and the actual output doesn't
282 match the expected output anymore, the test will fail.
284 If this is the case and you made an intentional change, check the
285 actual output for validity and correctness, copy it to
286 ``functional/expected/`` (overwriting the old expected output), and
287 commit the change.
289 .. [#] `Docutils XML` can be validated with
290    ``xmllint --dtdvalid docs/ref/docutils.dtd --noout *.xml``.