root/library/doc/local/unit_testing.dox @ 543

Revision 543, 8.1 kB (checked in by vbarta, 15 years ago)

added unit test documentation

Line 
1/*!
2\page unit_testing Unit testing of BDM
3
4Unit tests are simple, fast and easy to run tests of small pieces of
5code (the "units") designed to reassure developers that individual
6parts of the developped functionality behave as they're meant to. BDM
7being an object-oriented library, the independent units are classes,
8tested by calling their individual methods and comparing actual to
9expected results.
10
11Unit tests are especially useful for C++, where the simplest change
12can introduce (or uncover) a difficult-to-find bug. They also
13facilitate changing the internal structure of classes without
14modifying their external behavior - unit tests can be run before and
15after such a change to make sure the behavior had indeed stayed the
16same. More ambitiously, tests can be written before implementing new
17functionality, serving as its machine-checkable specification. Some
18authorities go so far as to demand that "not a single line of code" be
19written before having a failing unit test for it first, but BDM unit
20testing isn't that demanding - it does facilitate the style for
21developers who prefer it, but most BDM code is tested retroactively,
22with new tests suggested by various considerations (see below).
23
24\section Setup
25
26Unit tests of BDM use a 3rd-party unit testing framework, UnitTest++
27(from http://unittest-cpp.sourceforge.net/ ), which is designed
28specifically for C++, reasonably popular and maintained (for a C++
29unit testing framework), works on Unix, Windows and Mac, allows simple
30test addition and proved to be readily extensible. Sources of
31UnitTest++ are included in directory library/tests/unittest-cpp, so
32that they can be built as part of the normal BDM build, without the
33need for explicit installation and setup, and are lightly modified for
34specific requirements of BDM.
35
36Existing unit tests are collected into a testsuite executable (so that
37they can all easily be run together), whose sources are in
38library/tests. Apart from the top-level testsuite.cpp, testsuite
39sources are generally named <classname>_test.cpp, where
40<classname> is the class tested by the file (or a base class of
41classes tested by the file, when these derived classes are similar
42enough to be tested in the same way). BDM doesn't test abstract
43classes using mock-ups (although it does test some template
44instantiations on test-only classes) - all its abstract classes
45already have concrete children and methods of the base class can be
46tested on them.
47
48When run, testsuite prints to standard output the names of performed
49tests and error messages when they fail, which means, among other
50things, that it should generally be run from the command line - even
51on Windows. Note that while UnitTest++ does have support for more
52elaborate GUIs, this support hasn't been extended for BDM-specific use
53cases. Testsuite can also be run under debugging tools - not only
54under a debugger, but also valgrind for checking memory management,
55coverage tools etc.
56
57Specific tests can be run individually, by passing their name (or
58names) as a command-line argument to testsuite. Note that a test which
59behaves differently depending on which tests run before it is a bug in
60testsuite (this can happen e.g. when using RVs, which are global -
61it's probably best to include the tested class name in their names,
62although not all existing tests do that yet). Tests of sampling and
63other functionality depending on random inputs can occasionally fail.
64Whether that's due to skewed inputs, BDM bugs or too tight error
65limits is not clear. Testsuite aims for every test passing with at
66least 95% probability, but the quantification of error limits isn't
67finished yet.
68
69Not all tests depending on UnitTest++ are in testsuite. Performance
70experiments have their own executables - currently just
71square_mat_stress, for testing BDM matrix wrappers. square_mat_stress
72is more configurable than testsuite: it doesn't generate its own
73random data, but reads them from a configuration file (named
74agenda.cfg by default) generated by another executable,
75square_mat_prep. This allows generation (and checking) of test inputs
76for various numerically demanding scenarios, which are encapsulated in
77square_mat_prep's generators - that is, classes implementing its
78generator interface. generators themselves are configurable using the
79same scripting framework as other BDM classes, and both
80square_mat_prep and square_mat_stress have command-line parameters -
81run them with '-?' to see the list. Analogical support can be created
82for other classes which would benefit from it. Focus of stress tests
83is rather different from unit tests as generally understood - that a
84class has stress tests doesn't mean it shouldn't be tested separately
85inside testsuite.
86
87\section new_unit_tests Adding new unit tests
88
89Adding new unit tests is encouraged - it's a good way to get
90acquainted with the library, and there's always space for more
91tests.
92
93Unit tests are implemented by adding a block of code (it actually
94isn't just a function body, but it looks like one) starting with the
95TEST macro, whose argument is the test name:
96
97\code
98TEST ( test_egiw ) {
99        epdf_harness::test_config ( "egiw.cfg" );
100}
101\endcode
102
103"UnitTest++.h" should be included in every test file to have the macro
104defined.
105
106Tests are usually named test_<classname>, or
107test_<classname>_postfix when a class has more than one test:
108
109\code
110TEST ( test_egiw_1_2 ) {
111\endcode
112
113This is useful when a class should be thoroughly tested from multiple
114aspects, e.g. for different dimensions. Note that each test in
115testsuite must have a unique name, which must be a legal C++
116identifier.
117
118Testsuite has explicit support for testing classes derived from
119bdm::epdf and bdm::mpdf: bdm::epdf_harness and
120bdm::mpdf_harness. These classes run a list of tests on objects
121created from the specified configuration file (normally called
122<classname>.cfg) and check them against expected values also
123specified in that file, so that a test using harness is just a
124single-line call to its test_config method (as above).
125
126Unit tests can also be written explicitly. The test body is normal C++
127code, implementing the checks of actual against expected values using
128various CHECK_* macros defined by UnitTest++:
129
130\code
131        double summ = 0.0;
132        for ( int k = 0; k < n; k++ ) { // ALL b
133...
134        }
135
136        CHECK_CLOSE ( 1.0, summ, 0.1 );
137\endcode
138
139Note that when checking vectors or matrices, you should also include
140"mat_checks.h", which defines their comparison. Unit tests shouldn't
141require any user interaction and probably shouldn't output anything
142other than error reports via the UnitTest++ facilities - among other
143things, UnitTest++ uses C functions for its output, and therefore a
144test printing via std::cout and std::cerr may report in rather
145confusing mixed order.
146
147Developers wishing to add new tests can use various heuristics to come
148up with the specific functionality to test - a selection is described
149below, roughly in the order of usefulness.
150
151\subsection existing_bugs Eliciting existing bugs
152
153When the library has a bug, it may be useful to isolate the smallest
154possible code snippet which exhibits it - if not always, then at
155least as reliably as possible. Such a snippet is a good candidate for
156a unit test - it can be added either before the bug is fixed, or
157afterwards.
158
159\subsection tricky_parts Testing the code known to be tricky
160
161Every body of code has its dark corners - copy&pasted legacy code,
162highly optimized algorithms, complicated interactions. These should be
163tested first.
164
165\subsection unclear_behavior Documenting and stabilizing unclear behavior
166
167The library can behave in ways surprising its users without being
168necessarily "wrong". In such cases, it's most important that all
169maintainers agree on what the correct behavior should be (so that
170attempts to fix it don't run in circles). Unit tests constraining the
171contested functionality communicate very efficiently what their author
172thinks the behavior should (or shouldn't) be.
173
174\subsection coverage Increasing test coverage
175
176Ideally, unit tests should test every method of every class (there are
177higher ideals, but they're even less realistic). Failing that, at
178least some methods of every class should be tested. Coverage tools
179(e.g. gcov) can show which parts of the library are not exercised by
180testsuite.
181
182*/
Note: See TracBrowser for help on using the browser.