blob: 97026f81147a6980c1e216415c1efb0a0c8aaa69 [file] [log] [blame]
onqtame2df0ba2016-11-21 14:48:55 +02001== doctest - the lightest C++ unit testing framework
2
3doctest is a fully open source light and feature-rich C++98 / C++11 single-header testing framework for unit tests and TDD.
4
onqtamb6ac3f22017-01-18 14:12:44 +02005Web Site: https://github.com/onqtam/doctest
onqtame2df0ba2016-11-21 14:48:55 +02006Version tested: 1.1.3
7System requirements: C++98 or newer
8License & Pricing: MIT, free
onqtam38115fb2017-01-05 16:01:16 +02009Support: through the GitHub project page
onqtame2df0ba2016-11-21 14:48:55 +020010
11== Introduction
12
13A complete example with a self-registering test that compiles to an executable looks like this:
14
15#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
16#include "doctest.h"
17
18int fact(int n) { return n <= 1 ? n : fact(n - 1) * n; }
19
20TEST_CASE("testing the factorial function") {
21 CHECK(fact(0) == 1); // will fail
22 CHECK(fact(1) == 1);
23 CHECK(fact(2) == 2);
24 CHECK(fact(10) == 3628800);
25}
26
27And the output from that program is the following:
28
29[doctest] doctest version is "1.1.3"
30[doctest] run with "--help" for options
31========================================================
32main.cpp(6)
33testing the factorial function
34
35main.cpp(7) FAILED!
36 CHECK( fact(0) == 1 )
37with expansion:
38 CHECK( 0 == 1 )
39
40========================================================
41[doctest] test cases: 1 | 0 passed | 1 failed |
42[doctest] assertions: 4 | 3 passed | 1 failed |
43
onqtam38115fb2017-01-05 16:01:16 +020044Note how a standard C++ operator for equality comparison is used - doctest has one core assertion macro (it also has macros for less than, equals, greater than...) - yet the full expression is decomposed and the left and right values are logged. This is done with expression templates and C++ trickery. Also the test case is automatically registered - you don't need to manually insert it to a list.
onqtame2df0ba2016-11-21 14:48:55 +020045
onqtamb6ac3f22017-01-18 14:12:44 +020046Doctest is modeled after Catch [1] which is currently the most popular alternative for testing in C++ (along with googletest [5]) - check out the differences in the FAQ [7]. Currently a few things which Catch has are missing but doctest aims to eventually become a superset of Catch.
onqtame2df0ba2016-11-21 14:48:55 +020047
48== Motivation behind the framework - how is it different
49
onqtamb6ac3f22017-01-18 14:12:44 +020050doctest is inspired by the unittest {} functionality of the D programming language and Python's docstrings - tests can be considered a form of documentation and should be able to reside near the production code which they test (for example in the same source file a class is implemented).
51
52A few reasons you might want to do that:
53
54- Testing internals that are not exposed through the public API and headers of a module becomes easier.
55- Lower barrier for writing tests - you don't have to:
56 1. make a separate source file
57 2. include a bunch of stuff in it
58 3. add it to the build system
59 4. add it to source control
60 You can just write the tests for a class or a piece of functionality at the bottom of its source file - or even header file!
61- Faster iteration times - TDD becomes a lot easier.
62- Tests in the production code stay in sync and can be thought of as active documentation or up-to-date comments - showing how an API is used.
63
64The framework can still be used like any other even if the idea of writing tests in the production code doesn't appeal to you - but this is the biggest power of the framework - and nothing else comes close to being so practical in achieving this - details below.
65
66There are many other features [8] and a lot more are planned in the roadmap [9].
67
68This isn't possible (or at least practical) with any other testing framework for C++ - Catch [1], Boost.Test [2], UnitTest++ [3], cpputest [4], googletest [5] and many others [6].
onqtame2df0ba2016-11-21 14:48:55 +020069
onqtam38115fb2017-01-05 16:01:16 +020070What makes doctest different is that it is ultra light on compile times (by orders of magnitude - further details are in the "Compile time benchmarks" section) and is unintrusive.
onqtame2df0ba2016-11-21 14:48:55 +020071
72The key differences between it and the others are:
73
onqtam1687de82017-02-18 18:23:53 +020074- Ultra light - below 10ms of compile time overhead for including the header in a source file (compared to 250-460 ms for Catch) - see the "Compile time benchmarks" section
onqtame2df0ba2016-11-21 14:48:55 +020075- The fastest possible assertion macros - 50 000 asserts can compile for under 30 seconds (even under 10 sec)
onqtame2df0ba2016-11-21 14:48:55 +020076- Offers a way to remove everything testing-related from the binary with the DOCTEST_CONFIG_DISABLE identifier
77- Doesn't pollute the global namespace (everything is in the doctest namespace) and doesn't drag any headers with it
78- Doesn't produce any warnings even on the most aggressive warning levels for MSVC / GCC / Clang
79 * -Weverything for Clang
80 * /W4 for MSVC
onqtamb6ac3f22017-01-18 14:12:44 +020081 * -Wall -Wextra -pedantic and over 50 other flags not included in these!
onqtame2df0ba2016-11-21 14:48:55 +020082- Very portable and well tested C++98 - per commit tested on CI with over 220 different builds with different compilers and configurations (gcc 4.4-6.1 / clang 3.4-3.9 / MSVC 2008-2015, debug / release, x86/x64, linux / windows / osx, valgrind, sanitizers...)
83- Just one header and no external dependencies apart from the C / C++ standard library (which are used only in the test runner)
84
onqtam1687de82017-02-18 18:23:53 +020085So if doctest is included in 1000 source files (globally in a big project) the overall build slowdown will be only ~10 seconds. If Catch is used - this would mean 350+ seconds just for including the header everywhere.
onqtame2df0ba2016-11-21 14:48:55 +020086
onqtamb6ac3f22017-01-18 14:12:44 +020087If you have 50 000 asserts spread across your project (which is quite a lot) you should expect to see roughly 60-100 seconds of increased build time if using the normal expression-decomposing asserts or 10-40 seconds if you have used the fast form [11] of the asserts.
onqtame2df0ba2016-11-21 14:48:55 +020088
onqtamb6ac3f22017-01-18 14:12:44 +020089These numbers pale in comparison to the build times of a 1000 source file project. Further details are in the "Compile time benchmarks" section.
onqtame2df0ba2016-11-21 14:48:55 +020090
onqtamb6ac3f22017-01-18 14:12:44 +020091You also won't see any warnings or unnecessarily imported symbols from doctest - nor will you see a valgrind or a sanitizer error caused by the framework - it is truly transparent.
onqtame2df0ba2016-11-21 14:48:55 +020092
93== The main() entry point
94
95As we saw in the example above - a main() entry point for the program can be provided by the framework. If however you are writing the tests in your production code you probably already have a main() function. The following code example shows how doctest is used from a user main():
96
97#define DOCTEST_CONFIG_IMPLEMENT
98#include "doctest.h"
99int main(int argc, char** argv) {
100 doctest::Context ctx;
onqtamcfce1882017-04-02 17:31:55 +0300101 // !!! THIS IS JUST AN EXAMPLE SHOWING HOW DEFAULTS/OVERRIDES ARE SET !!!
onqtame2df0ba2016-11-21 14:48:55 +0200102 ctx.setOption("abort-after", 5); // default - stop after 5 failed asserts
103 ctx.applyCommandLine(argc, argv); // apply command line - argc / argv
104 ctx.setOption("no-breaks", true); // override - don't break in the debugger
105 int res = ctx.run(); // run test cases unless with --no-run
106 if(ctx.shouldExit()) // query flags (and --exit) rely on this
107 return res; // propagate the result of the tests
108 // your code goes here
109 return res; // + your_program_res
110}
111
112With this setup the following 3 scenarios are possible:
113- running only the tests (with the --exit option)
114- running only the user code (with the --no-run option)
115- running both the tests and the user code
116
117This must be possible if you are going to write the tests directly in the production code.
118
119Also this example shows how defaults and overrides can be set for command line options.
120
121Note that the DOCTEST_CONFIG_IMPLEMENT or DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN identifiers should be defined before including the framework header - but only in one source file - where the test runner will get implemented. Everywhere else just include the header and write some tests. This is a common practice for single-header libraries that need a part of them to be compiled in one source file (in this case the test runner).
122
123== Removing everything testing-related from the binary
124
125You might want to remove the tests from your production code when building the release build that will be shipped to customers. The way this is done using doctest is by defining the DOCTEST_CONFIG_DISABLE preprocessor identifier in your whole project.
126
127The effect that identifier has on the TEST_CASE macro for example is the following - it gets turned into an anonymous template that never gets instantiated:
128
129#define TEST_CASE(name) \
130 template <typename T> \
131 static inline void ANONYMOUS(ANON_FUNC_)()
132
133This means that all test cases are trimmed out of the resulting binary - even in Debug mode! The linker doesn't ever see the anonymous test case functions because they are never instantiated.
134
135The ANONYMOUS() macro is used to get unique identifiers each time it's called - it uses the __COUNTER__ preprocessor macro which returns an integer with 1 greater than the last time each time it gets used. For example:
136
137int ANONYMOUS(ANON_VAR_); // int ANON_VAR_5;
138int ANONYMOUS(ANON_VAR_); // int ANON_VAR_6;
139
140== Subcases - the easiest way to share setup / teardown code between test cases
141
142Suppose you want to open a file in a few test cases and read from it. If you don't want to copy / paste the same setup code a few times you might use the Subcases mechanism of doctest.
143
144TEST_CASE("testing file stuff") {
145 printf("opening the file\n");
onqtam38115fb2017-01-05 16:01:16 +0200146 std::ifstream is ("test.txt", std::ifstream::binary);
onqtame2df0ba2016-11-21 14:48:55 +0200147
148 SUBCASE("seeking in file") {
149 printf("seeking\n");
onqtam38115fb2017-01-05 16:01:16 +0200150 // is.seekg()
onqtame2df0ba2016-11-21 14:48:55 +0200151 }
152 SUBCASE("reading from file") {
153 printf("reading\n");
onqtam38115fb2017-01-05 16:01:16 +0200154 // is.read()
onqtame2df0ba2016-11-21 14:48:55 +0200155 }
onqtam38115fb2017-01-05 16:01:16 +0200156 printf("closing... (by the destructor)\n");
onqtame2df0ba2016-11-21 14:48:55 +0200157}
158
159The following text will be printed:
160
161opening the file
162seeking
onqtam38115fb2017-01-05 16:01:16 +0200163closing... (by the destructor)
onqtame2df0ba2016-11-21 14:48:55 +0200164opening the file
165reading
onqtam38115fb2017-01-05 16:01:16 +0200166closing... (by the destructor)
onqtame2df0ba2016-11-21 14:48:55 +0200167
168As you can see the test case was entered twice - and each time a different subcase was entered. Subcases can also be infinitely nested. The execution model resembles a DFS traversal - each time starting from the start of the test case and traversing the "tree" until a leaf node is reached (one that hasn't been traversed yet) - then the test case is exited by popping the stack of entered nested subcases.
169
onqtamb6ac3f22017-01-18 14:12:44 +0200170== Examples of how to embed tests in production code
171
172If shipping libraries with tests - it is a good idea to add a tag in your test case names (like this: TEST_CASE("[the_lib] testing foo")) so the user can easily filter them out with --test-case-exclude=*[the_lib]* if he wishes to.
173
174- If you are shipping a header-only library there are mainly 2 options:
175
1761. You could surround your tests with an ifdef to check if doctest is included before your headers - like this:
177
178// fact.h
179#pragma once
180
181inline int fact(int n) { return n <= 1 ? n : fact(n - 1) * n; }
182
183#ifdef DOCTEST_LIBRARY_INCLUDED
184TEST_CASE("[fact] testing the factorial function") {
185 CHECK(fact(0) == 1); // will fail
186 CHECK(fact(1) == 1);
187 CHECK(fact(2) == 2);
188 CHECK(fact(10) == 3628800);
189}
190#endif // DOCTEST_LIBRARY_INCLUDED
191
1922. You could use a preprocessor identifier (like FACT_WITH_TESTS) to conditionally use the tests - like this:
193
194// fact.h
195#pragma once
196
197inline int fact(int n) { return n <= 1 ? n : fact(n - 1) * n; }
198
199#ifdef FACT_WITH_TESTS
200
201#ifndef DOCTEST_LIBRARY_INCLUDED
202#include "doctest.h"
203#endif // DOCTEST_LIBRARY_INCLUDED
204
205TEST_CASE("[fact] testing the factorial function") {
206 CHECK(fact(0) == 1); // will fail
207 CHECK(fact(1) == 1);
208 CHECK(fact(2) == 2);
209 CHECK(fact(10) == 3628800);
210}
211#endif // FACT_WITH_TESTS
212
213In both of these cases the user of the header-only library will have to implement the test runner of the framework somewhere in his executable/shared object.
214
215- If you are developing an end product and not a library for developers - then you can just mix code and tests and implement the test runner like described in the section "The main() entry point".
216
217- If you are developing a library which is not header-only - you could again write tests in your headers like shown above, and you could also make use of the DOCTEST_CONFIG_DISABLE identifier to optionally remove the tests from the source files when shipping it - or figure out a custom scheme like the use of a preprocessor identifier to optionally ship the tests - MY_LIB_WITH_TESTS.
218
onqtame2df0ba2016-11-21 14:48:55 +0200219== Compile time benchmarks
220
221So there are 3 types of compile time benchmarks that are relevant for doctest:
222- cost of including the header
223- cost of assertion macros
224- how much the build times drop when all tests are removed with the DOCTEST_CONFIG_DISABLE identifier
225
226In summary:
onqtam1687de82017-02-18 18:23:53 +0200227- Including the doctest header costs under 10ms compared to 250-460 ms of Catch - so doctest is 25-50 times lighter
onqtame2df0ba2016-11-21 14:48:55 +0200228- 50 000 asserts compile for roughly 60 seconds which is around 25% faster than Catch
onqtamb6ac3f22017-01-18 14:12:44 +0200229- 50 000 asserts can compile for as low as 30 seconds (or even 10) if alternative assert macros [11] are used (for power users)
230- 50 000 asserts spread in 500 test cases just vanish when disabled with DOCTEST_CONFIG_DISABLE - all of it takes less than 2 seconds!
231
232The lightness of the header was achieved by forward declaring everything and not including anything in the main part of the header. There are includes in the test runner implementation part of the header but that resides in only one translation unit - where the library gets implemented (by defining the DOCTEST_CONFIG_IMPLEMENT preprocessor identifier before including it).
233
234Regarding the cost of asserts - note that this is for trivial asserts comparing 2 integers - if you need to construct more complex objects and have more setup code for your test cases then there will be an additional amount of time spent compiling - this depends very much on what is being tested. A user of doctest provides a real world example of this in his article [12].
onqtame2df0ba2016-11-21 14:48:55 +0200235
onqtam38115fb2017-01-05 16:01:16 +0200236In the benchmarks page [10] of the project documentation you can see the setup and more details for the benchmarks.
onqtame2df0ba2016-11-21 14:48:55 +0200237
238== Conclusion
239
240The doctest framework is really easy to get started with and is fully transparent and unintrusive - including it and writing tests will be unnoticeable both in terms of compile times and integration (warnings, build system, etc). Using it will speed up your development process as much as possible - no other framework is so easy to use!
241
onqtamb6ac3f22017-01-18 14:12:44 +0200242Note that Catch 2 is on it's way (not public yet) and when it is released there will be a new set of benchmarks.
243
onqtame2df0ba2016-11-21 14:48:55 +0200244The development of doctest is supported with donations.
245
246[1] https://github.com/philsquared/Catch
247[2] http://www.boost.org/doc/libs/1_60_0/libs/test/doc/html/index.html
248[3] https://github.com/unittest-cpp/unittest-cpp
249[4] https://github.com/cpputest/cpputest
250[5] https://github.com/google/googletest
251[6] https://en.wikipedia.org/wiki/List_of_unit_testing_frameworks#C.2B.2B
252[7] https://github.com/onqtam/doctest/blob/master/doc/markdown/faq.md#how-is-doctest-different-from-catch
253[8] https://github.com/onqtam/doctest/blob/master/doc/markdown/features.md
254[9] https://github.com/onqtam/doctest/blob/master/doc/markdown/roadmap.md
255[10] https://github.com/onqtam/doctest/blob/master/doc/markdown/benchmarks.md
onqtamb6ac3f22017-01-18 14:12:44 +0200256[11] https://github.com/onqtam/doctest/blob/master/doc/markdown/assertions.md#fast-asserts
257[12] http://baptiste-wicht.com/posts/2016/09/blazing-fast-unit-test-compilation-with-doctest-11.html