FAQ

How is doctest different from Catch?

Pros of doctest:

Aside from everything mentioned so far doctest has some features (like test suites and decorators) which Catch doesn't.

Missing stuff:

But these things (and more!) are planned in the roadmap!

doctest can be thought of as a very polished, light, stable and clean subset (or reimplementation) of Catch but this might change in the future as more features are added.

Also checkout this table that compares doctest / Catch / lest.

A quick and easy way to migrate most of your Catch tests to doctest is to change the TEST_CASE (if using tags) and SECTION macros as follows:

#include "path/to/doctest.h"

#define SECTION(name) DOCTEST_SUBCASE(name)

// only if tags are used: will concatenate them to the test name string literal
#undef TEST_CASE
#define TEST_CASE(name, tags) DOCTEST_TEST_CASE(tags " " name)

// catch exposes this by default outside of its namespace
using doctest::Approx;

How is doctest different from Google Test?

Here are a couple of differences:

but there are also some areas in which doctest is lacking:

The areas where doctest is behind are planned for improvement in the future. There are many other smaller differences - it would be impractical to cover them all.

How to get the best compile-time performance with the framework?

The DOCTEST_CONFIG_SUPER_FAST_ASSERTS config option yields the fastest possible compile times (up to 31-91%). Also the expression-decomposing template machinery can be skipped by using the binary asserts.

There are only 2 tiny drawbacks of using this config option:

These 2 things can be considered negligible and totally worth it if you are dealing mainly with expressions unlikely to throw exceptions and all the tests usually pass (you don't need to navigate often to a failing assert with a debugger attached).

Is doctest thread-aware?

Most macros/functionality is safe to use in a multithreaded context: assertion and logging macros can be safely used from multiple threads spawned from a single test case. This however does not mean that multiple test cases can be ran in parallel - test cases are still ran serially. Subcases should also be used only from the test runner thread and all threads spawned in a subcase ought to be joined before the end of that subcase and no new subcases should be entered while other threads with doctest assertions in them are still running - not following these instructions will lead to crashes (example in here). Also note that logged context in one thread will not be used/printed when asserts from another thread fail - logged context is thread-local.

There is also an option to run a range of tests from an executable - so tests can be ran in parallel by invoking the process multiple times with different ranges - see the example python script.

Is mocking supported?

doctest doesn't support mocking but should be easy to integrate with third-party libraries such as:

by using the logging macros such as ADD_FAIL_AT(file, line, message)

Why are my tests in a static library not getting registered?

This is a common problem among libraries with self-registering code and it affects all modern compilers on all platforms.

The problem is that when a static library is being linked to a binary (executable or dll) - only object files from the static library that define a symbol being required from the binary will get pulled in (this is a linker/dependency optimization).

A way to solve this in CMake is to use object libraries instead of static libraries - like this:

add_library(with_tests OBJECT src_1.cpp src_2.cpp src_3.cpp ...)

add_library(dll SHARED $<TARGET_OBJECTS:with_tests> dll_src_1.cpp ...)
add_executable(exe $<TARGET_OBJECTS:with_tests> exe_src_1.cpp ...)

Thanks to pthom for suggesting this.

As an alternative I have created a CMake function that forces every object file from a static library to be linked into a binary target - it is called doctest_force_link_static_lib_in_target(). It is unintrusive - no source file gets changed - everything is done with compiler flags per source files. An example project using it can be found here - the commented part of the CMakeLists.txt file.

It doesn't work in 2 scenarios:

You can also checkout this repository for a different solution: pthom/doctest_registerlibrary.

A compiler-specific solution for MSVC is to use the /OPT:NOREF linker flag (thanks to lectem for reporting it!). Another option is to look at /wholearchive for MSVC.

Why is comparing C strings (char*) actually comparing pointers?

doctest by default treats char* as normal pointers. Using the DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING changes that.

How to write tests in header-only libraries?

There are 2 options:

Also note that it would be 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 they wish to.

Does the framework use exceptions?

Yes - but they can be disabled - see the DOCTEST_CONFIG_NO_EXCEPTIONS config identifier.

Why do I get compiler errors in STL headers when including the doctest header?

Try using the DOCTEST_CONFIG_USE_STD_HEADERS configuration identifier.

Can different versions of the framework be used within the same binary (executable/dll)?

Currently no. Single header libraries like stb have this as an option (everything gets declared static - making it with internal linkage) but it isn't very logical for doctest - the main point is to write tests in any source file of the project and have the test runner implemented in only one source file.

Why is doctest using macros?

Aren't they evil and not modern? - Check out the answer Phil Nash gives to this question here (the creator of Catch).

How to use with multiple files?

All you need to do is define either DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN or DOCTEST_CONFIG_IMPLEMENT in only ONE of the source files just before including the doctest header - in all other source files you just include the header and use the framework. The difference between the two is that one of them provides a main() entry point - for more info on that please refer to The main() entry point.


Home