blob: 423dc2352e7dfa0d36c778d57d406decf0bee837 [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
5It 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. This isn't possible (or at least practical) with any other testing framework for C++.
6
7Web Site: https://github.com/onqtam/doctest
8Version tested: 1.1.3
9System requirements: C++98 or newer
10License & Pricing: MIT, free
11Support: as issues through the GitHub project page
12
13== Introduction
14
15A complete example with a self-registering test that compiles to an executable looks like this:
16
17#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
18#include "doctest.h"
19
20int fact(int n) { return n <= 1 ? n : fact(n - 1) * n; }
21
22TEST_CASE("testing the factorial function") {
23 CHECK(fact(0) == 1); // will fail
24 CHECK(fact(1) == 1);
25 CHECK(fact(2) == 2);
26 CHECK(fact(10) == 3628800);
27}
28
29And the output from that program is the following:
30
31[doctest] doctest version is "1.1.3"
32[doctest] run with "--help" for options
33========================================================
34main.cpp(6)
35testing the factorial function
36
37main.cpp(7) FAILED!
38 CHECK( fact(0) == 1 )
39with expansion:
40 CHECK( 0 == 1 )
41
42========================================================
43[doctest] test cases: 1 | 0 passed | 1 failed |
44[doctest] assertions: 4 | 3 passed | 1 failed |
45
46Note how a standard C++ operator for equality comparison is used - doctest has one core assertion macro (it also has 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.
47
48Doctest is modeled after Catch [1] which is currently the most popular alternative for testing in C++ - check out the differences in the FAQ [7]. Currently a few things which Catch has are missing but doctest will eventually become a superset of Catch.
49
50== Motivation behind the framework - how is it different
51
52There are many C++ testing frameworks - Catch [1], Boost.Test [2], UnitTest++ [3], cpputest [4], googletest [5] and many other [6].
53
54What makes doctest different is that it is ultra light on compile times (by orders of magnitude) and is unintrusive.
55
56The key differences between it and the others are:
57
58- Ultra light - below 10ms of compile time overhead for including the header in a source file
59- The fastest possible assertion macros - 50 000 asserts can compile for under 30 seconds (even under 10 sec)
60- Subcases - an intuitive way to share common setup and teardown code for test cases (alternative to fixtures)
61- Offers a way to remove everything testing-related from the binary with the DOCTEST_CONFIG_DISABLE identifier
62- Doesn't pollute the global namespace (everything is in the doctest namespace) and doesn't drag any headers with it
63- Doesn't produce any warnings even on the most aggressive warning levels for MSVC / GCC / Clang
64 * -Weverything for Clang
65 * /W4 for MSVC
66 * -Wall -Wextra -pedantic and over 50 other flags!
67- 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...)
68- Just one header and no external dependencies apart from the C / C++ standard library (which are used only in the test runner)
69
70== The unique ability that this framework provides
71
72All the previously listed pros allow the framework to be used in more ways than any other - tests can be written directly in the production code!
73
74- This makes the barrier for writing tests much lower - you don't have to: 1. make a separate source file 2. include a bunch of stuff in it 3. add it to the build system and 4. add it to source control - 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!
75- Tests in the production code can be thought of as documentation or up-to-date comments - showing how an API is used (correctness enforced by the compiler).
76- Testing internals that are not exposed through the public API and headers becomes easier.
77- Test-driven development in C++ has never been easier!
78
79The 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 - which nothing else offers!
80
81There are many other features [8] and a lot more are planned in the roadmap [9].
82
83== The main() entry point
84
85As 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():
86
87#define DOCTEST_CONFIG_IMPLEMENT
88#include "doctest.h"
89int main(int argc, char** argv) {
90 doctest::Context ctx;
91 ctx.setOption("abort-after", 5); // default - stop after 5 failed asserts
92 ctx.applyCommandLine(argc, argv); // apply command line - argc / argv
93 ctx.setOption("no-breaks", true); // override - don't break in the debugger
94 int res = ctx.run(); // run test cases unless with --no-run
95 if(ctx.shouldExit()) // query flags (and --exit) rely on this
96 return res; // propagate the result of the tests
97 // your code goes here
98 return res; // + your_program_res
99}
100
101With this setup the following 3 scenarios are possible:
102- running only the tests (with the --exit option)
103- running only the user code (with the --no-run option)
104- running both the tests and the user code
105
106This must be possible if you are going to write the tests directly in the production code.
107
108Also this example shows how defaults and overrides can be set for command line options.
109
110Note 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).
111
112== Removing everything testing-related from the binary
113
114You 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.
115
116The 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:
117
118#define TEST_CASE(name) \
119 template <typename T> \
120 static inline void ANONYMOUS(ANON_FUNC_)()
121
122This 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.
123
124The 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:
125
126int ANONYMOUS(ANON_VAR_); // int ANON_VAR_5;
127int ANONYMOUS(ANON_VAR_); // int ANON_VAR_6;
128
129== Subcases - the easiest way to share setup / teardown code between test cases
130
131Suppose 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.
132
133TEST_CASE("testing file stuff") {
134 printf("opening the file\n");
135 FILE* fp = fopen("path/to/file", "r");
136
137 SUBCASE("seeking in file") {
138 printf("seeking\n");
139 // fseek()
140 }
141 SUBCASE("reading from file") {
142 printf("reading\n");
143 // fread()
144 }
145 printf("closing...\n");
146 fclose(fp);
147}
148
149The following text will be printed:
150
151opening the file
152seeking
153closing...
154opening the file
155reading
156closing...
157
158As 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.
159
160== Compile time benchmarks
161
162So there are 3 types of compile time benchmarks that are relevant for doctest:
163- cost of including the header
164- cost of assertion macros
165- how much the build times drop when all tests are removed with the DOCTEST_CONFIG_DISABLE identifier
166
167In summary:
168- Including the doctest header costs around 10ms compared to 430ms of Catch - so doctest is 25-50 times lighter
169- 50 000 asserts compile for roughly 60 seconds which is around 25% faster than Catch
170- 50 000 asserts can compile for as low as 10 seconds if alternative assert macros are used (for power users)
171- 50 000 asserts spread in 500 test cases just vanish when disabled with DOCTEST_CONFIG_DISABLE - less than 2 seconds!
172
173In the benchmarks page [10] you can see the setup and more details for the benchmarks.
174
175== Conclusion
176
177The 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!
178
179The development of doctest is supported with donations.
180
181[1] https://github.com/philsquared/Catch
182[2] http://www.boost.org/doc/libs/1_60_0/libs/test/doc/html/index.html
183[3] https://github.com/unittest-cpp/unittest-cpp
184[4] https://github.com/cpputest/cpputest
185[5] https://github.com/google/googletest
186[6] https://en.wikipedia.org/wiki/List_of_unit_testing_frameworks#C.2B.2B
187[7] https://github.com/onqtam/doctest/blob/master/doc/markdown/faq.md#how-is-doctest-different-from-catch
188[8] https://github.com/onqtam/doctest/blob/master/doc/markdown/features.md
189[9] https://github.com/onqtam/doctest/blob/master/doc/markdown/roadmap.md
190[10] https://github.com/onqtam/doctest/blob/master/doc/markdown/benchmarks.md
191