fixed malformed hyperlink target
[docutils.git] / docs / dev / testing.txt
blob681cb7ae9adedc87a6828d281c4a74d7cb256ab5
1 ===================
2  Docutils_ Testing
3 ===================
5 :Author: Felix Wiemann
6 :Revision: $Revision$
7 :Date: $Date$
8 :Copyright: This document has been placed in the public domain.
10 .. _Docutils: http://docutils.sourceforge.net/
12 .. contents::
14 This document describes how the tests are organized and how to add new
15 tests or modify existing tests.
18 Unit Tests
19 ==========
21 Unit tests test single functions or modules (i.e. whitebox testing).
23 If you are implementing a new feature, be sure to write a test case
24 covering its functionality.  It happens very frequently that your
25 implementation (or even only a part of it) doesn't work with an older
26 (or even newer) Python version, and the only reliable way to detect
27 those cases is using tests.
29 Often, it's easier to write the test first and then implement the
30 functionality required to make the test pass.
33 Writing New Tests
34 -----------------
36 When writing new tests, it very often helps to see how a similar test
37 is implemented.  For example, the files in the
38 ``test_parsers/test_rst/`` directory all look very similar.  So when
39 adding a test, you don't have to reinvent the wheel.
41 If there is no similar test, you can write a new test from scratch
42 using Python's ``unittest`` module.  For an example, please have a
43 look at the following imaginary ``test_square.py``::
45     #! /usr/bin/env python
47     # Author: your name
48     # Contact: your email address
49     # Revision: $Revision$
50     # Date: $Date$
51     # Copyright: This module has been placed in the public domain.
53     """
54     Test module for docutils.square.
55     """
57     import unittest
58     import docutils.square
61     class SquareTest(unittest.TestCase):
63         def test_square(self):
64             self.assertEqual(docutils.square.square(0), 0)
65             self.assertEqual(docutils.square.square(5), 25)
66             self.assertEqual(docutils.square.square(7), 49)
68         def test_square_root(self):
69             self.assertEqual(docutils.square.sqrt(49), 7)
70             self.assertEqual(docutils.square.sqrt(0), 0)
71             self.assertRaises(docutils.square.SquareRootError,
72                               docutils.square.sqrt, 20)
75     if __name__ == '__main__':
76         unittest.main()
78 For more details on how to write tests, please refer to the
79 documentation of the ``unittest`` module.
82 .. _functional:
84 Functional Tests
85 ================
87 The directory ``test/functional/`` contains data for functional tests.
89 Performing functional testing means testing the Docutils system as a
90 whole (i.e. blackbox testing).
93 Directory Structure
94 -------------------
96 + ``functional/`` The main data directory.
98   + ``input/`` The input files.
100     - ``some_test.txt``, for example.
102   + ``output/`` The actual output.
104     - ``some_test.html``, for example.
106   + ``expected/`` The expected output.
108     - ``some_test.html``, for example.
110   + ``tests/`` The config files for processing the input files.
112     - ``some_test.py``, for example.
114     - ``_default.py``, the `default configuration file`_.
117 The Testing Process
118 -------------------
120 When running ``test_functional.py``, all config files in
121 ``functional/tests/`` are processed.  (Config files whose names begin
122 with an underscore are ignored.)  The current working directory is
123 always Docutils' main test directory (``test/``).
125 For example, ``functional/tests/some_test.py`` could read like this::
127     # Source and destination file names.
128     test_source = "some_test.txt"
129     test_destination = "some_test.html"
131     # Keyword parameters passed to publish_file.
132     reader_name = "standalone"
133     parser_name = "rst"
134     writer_name = "html"
135     settings_overrides['output-encoding'] = 'utf-8'
136     # Relative to main ``test/`` directory.
137     settings_overrides['stylesheet_path'] = '../tools/stylesheets/default.css'
139 The two variables ``test_source`` and ``test_destination`` contain the
140 input file name (relative to ``functional/input/``) and the output
141 file name (relative to ``functional/output/`` and
142 ``functional/expected/``).  Note that the file names can be chosen
143 arbitrarily.  However, the file names in ``functional/output/`` *must*
144 match the file names in ``functional/expected/``.
146 All other variables are passed as keyword arguments to
147 ``docutils.core.publish_file``, so you can set reader, parser,
148 writer and anything else you want to configure.
150 Note that ``settings_overrides`` is already initialized as a
151 dictionary *before* the execution of the config file.
154 Creating New Tests
155 ------------------
157 In order to create a new test, put the input test file into
158 ``functional/input/``.  Then create a config file in
159 ``functional/tests/`` which sets at least input and output file names,
160 reader, parser and writer.
162 Now run ``test_functional.py``.  The test will fail, of course,
163 because you do not have an expected output yet.  However, an output
164 file will have been generated in ``functional/output/``.  Check this
165 output file for validity and correctness.  Then copy the file to
166 ``functional/expected/``.
168 If you rerun ``test_functional.py`` now, it should pass.
170 If you run ``test_functional.py`` later and the actual output doesn't
171 match the expected output anymore, the test will fail.
173 If this is the case and you made an intentional change, check the
174 actual output for validity and correctness, copy it to
175 ``functional/expected/`` (overwriting the old expected output), and
176 commit the change.
179 .. _default configuration file:
181 The Default Configuration File
182 ------------------------------
184 The file ``functional/tests/_default.py`` contains default settings.
185 It is executed just before the actual configuration files, which has
186 the same effect as if the contents of ``_default.py`` were prepended
187 to every configuration file.