# doctest: the C++ testing header that stays out of your build's way

> A single-header C++ test framework whose pitch is compile time and its willingness to let tests live in production files. Here is how that works, and where Catch2 is the better choice.

**doctest/doctest** — The fastest feature-rich C++11/14/17/20/23 single-header testing framework

- Repository: https://github.com/doctest/doctest
- Website: https://bit.ly/doctest-docs
- Stars: 6,866 · Forks: 701
- Language: C++
- License: MIT
- Published: 2026-10-06 · Updated: 2026-10-06 · Language: en
- Canonical page: https://hysenlabs.com/projects/doctest-doctest

## What a single header means for your build

The whole framework arrives as one file. The README's download badge points straight at `doctest/doctest.h`, and the badge for language support lists C++11, 14, 17, 20 and 23. There is a separate tag, 1.2.9, kept for C++98, which tells you the framework predates the current standard and has to keep compiling against compilers that were already old when it started.

Getting it into a project is a matter of cloning and building, or of vendoring that one file.

```bash
git clone https://github.com/doctest/doctest
cd doctest
```

The claim driving all of this is compile time, and the README is unusually direct about it: it says doctest is the fastest in both compile times and runtime compared to other feature-rich alternatives, and it links a benchmarks document for the numbers. It breaks that cost down into the two places it matters, the cost of including the header and the cost of writing an assert. That is the honest framing, because in C++ a testing framework that is cheap to include changes what you are willing to test.

The escape hatch is a single preprocessor identifier. Everything testing-related can be compiled out with `DOCTEST_CONFIG_DISABLE`, which is what makes it possible to leave tests in a production translation unit without paying for them in a release build.

## Writing tests in the same file as the code

The second claim is the one that makes doctest unusual. The README puts it plainly: tests can be written directly in the production code, framed as documentation that lives next to what it documents. The listed payoff is that you do not have to create a separate source file, include a pile of headers, add it to the build system, and commit it. You write a test at the bottom of the file, or even in a header.

The examples directory shows how concrete this gets. There are separate entries for `concurrency.cpp`, for asserts used outside of a testing context, and for an executable with a DLL and a plugin. That last category addresses something most frameworks leave awkward: two binaries can share one test registry, so a test defined in a shared object runs in the host binary's runner.

The design is not free. A test in a header means the test code is compiled into every translation unit that includes it unless the disable identifier is defined first, and it means your public headers carry test code unless you are careful about where you put it. The README says the framework also works like any other without mixing production code and tests, and points at its features document for that mode. Both are supported. The question is which one your team actually wants.

The thread-safety claim is narrower than it first appears. Asserts can be used from multiple threads spawned from a single test case, which is not the same as being able to register tests from several threads at once.

## Build system support across four toolchains

The repository tree carries build files for four ecosystems, which is a better signal of adoption than any of the badge counts. There is `CMakeLists.txt` and `meson.build` for the two C++ build systems most people reach for, and `BUILD.bazel`, `MODULE.bazel` and `WORKSPACE.bazel` for Bazel, including a module definition and a workspace definition so either the old or the new dependency model works.

There is also `doctest.pc.in`, a pkg-config template. Its presence means the framework can be located the way any installed library is, which matters if you install doctest system wide rather than vendoring it. The examples directory goes further and includes `installed_doctest_cmake`, which covers the case where you have already installed the library somewhere on your machine and want CMake to find it instead of building it from source.

The `examples/` folder is also where the unusual integrations live. Beyond `mpi/` there is `exe_with_static_libs`, `combining_the_same_tests_built_differently_in_multiple_shared_objects`, and a `range_based_execution.py` script, which suggests the project supports running a range of tests in a pipeline rather than only running everything.

For development on the framework itself rather than on a project using it, `.clang-format`, `.clang-tidy`, `.pre-commit-config.yaml` and `.editorconfig` at the root say that formatting and static analysis are enforced through the usual tooling, and `scripts/` holds the benchmark data the README links to.

## Where doctest sits against Catch2 and GoogleTest

The README does the honest thing and names its competitors, including Catch2, Boost.Test, UnitTest++, CppUTest and GoogleTest. It then says doctest is modelled after Catch and that parts of the code were taken directly, linking a section of its own FAQ that lists the differences. There is also a comparison table in a third-party repository that puts doctest, Catch2 and Caught_LEST next to each other, since all three share a design lineage.

So the honest comparison is not doctest against everything. It is doctest against Catch2, on a small number of axes. The first is compile time, where doctest argues it wins and links benchmarks. The second is whether tests live in production files, which Catch2 also permits, so this is closer than the marketing suggests. The third is scope: GoogleTest and Boost.Test come with more surrounding machinery, and doctest does not attempt to.

The features list in the README is where the trade shows up honestly. It claims warning-free compilation on the most aggressive warning levels for MSVC, GCC and Clang, no global namespace pollution with everything inside `doctest::`, no headers dragged in alongside, portability with over 100 CI builds covering static analysis and sanitizers, and the disable identifier. Those are real and checkable claims. What is not on the list is a mocking facility or a rich set of composable matchers, which is the gap you would notice first if you are moving from a heavier framework.

## Configuration and removing the framework entirely

The configuration story is documented separately from the features, in a configuration document the README links from the disable identifier. Because the framework is a single header, configuration is a matter of defines set before the include rather than a runtime object, and the ones that matter most are the ones that remove code.

```cpp
DOCTEST_CONFIG_DISABLE
```

That is the whole mechanism for a release build: define it before the header is included and the testing code is not there. The complementary identifiers in the same document control the runner, which assertion levels are compiled in, and how the output is formatted. There is a related idea for asserts outside a test context, which the README lists as a supported use: doctest can be used as a general purpose assert library in code that is not under test, with an example in the examples tree.

What this configuration model does not give you is a runtime toggle. You cannot turn doctest off in a deployed binary without a rebuild, and you cannot turn parts of it off at startup. For most projects that is the right trade, because the cost being avoided is compile time rather than memory. It is worth knowing before you commit to embedding the header widely.

## Release cadence and what the changelog says

There are three releases in the recent history and they are small. Version 2.5.1, published 2026-04-03, removed unnecessary link options from `doctest.pc.in` and nothing else. Version 2.5.2, published 2026-04-14, switched the `doctest_with_main` CMake target to a single source file.

Version 2.5.3, published 2026-07-06, is the substantive one, and it is almost entirely about compilers rather than features. It bumps MSVS to Visual Studio 18 2026, drops a deprecated `-Wstrict-overflow=1` flag for gcc-17 and later, adds a dedicated wrapper file for `windows.h`, and fixes several things around exception translation and `std::uncaught_exceptions`, including a check for `_MSC_VER` and `_LIBCPP_VERSION`.

That pattern is worth reading as a signal. A header-only framework that claims broad compiler support spends its releases chasing new compiler versions, warning levels and standard library edge cases. There is no feature work in any of these three releases. If you are choosing on features alone, this project will look quiet. If you are choosing on whether the thing will keep compiling on your toolchain in two years, that is the same team paying attention.

The project publishes under MIT, ships a `CHANGELOG.md`, and the last push was on 2026-08-29 on the master branch. There is also a dev branch with its own CI badge in the README.

## Conclusion

doctest is the right pick when compile time is your bottleneck or when you want tests sitting next to the implementation rather than in a separate target. It gives up the richer matcher library and the mocking story that a larger framework provides, and the project says so by linking a comparison with Catch2 in its own FAQ. Clone it, add the single header, and put one test case next to a class to see whether the compile times hold up on your toolchain. Version 2.5.3 shipped on 2026-07-06 and the last push was on 2026-08-29, so both the release line and the branch are current.

## FAQ

### How do I install doctest in a C++ project?

Clone the repository and either build it or copy the single header into your project. The README's download link points at doctest/doctest.h directly, and the tree carries CMakeLists.txt, meson.build and the Bazel files, so you can use whichever build system you already have. There is also a doctest.pc.in template for pkg-config and an installed_doctest_cmake example for the system-wide case.

### Can I write doctest tests inside my production source files?

That is the main design goal. The README argues you should not have to create a separate source file, add includes and wire it into the build, and points at an example of asserts used outside of a testing context. Define DOCTEST_CONFIG_DISABLE before including the header and the testing code is compiled out entirely.

### How does doctest compare to Catch2 and GoogleTest?

The README says doctest is modelled after Catch and that parts of the code were taken directly, then links its own FAQ section listing the differences. The practical axes are compile time, where doctest claims to be fastest with linked benchmarks, and feature scope, where GoogleTest and Boost.Test bring more machinery than doctest attempts to provide.

### Is doctest the same as the doctest module in Python?

No. This repository is a C++ testing framework distributed as a single header for C++11 through C++23. Python has its own unrelated doctest module that runs examples embedded in docstrings, and the two share nothing but a name.

## Sources

- [doctest/doctest on GitHub](https://github.com/doctest/doctest)
- [License: MIT](https://github.com/doctest/doctest/blob/master/LICENSE)
- [Project website](https://bit.ly/doctest-docs)
- [README](https://github.com/doctest/doctest/blob/master/README.md)
- [Releases](https://github.com/doctest/doctest/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/doctest-doctest
