# Catch2 v3 stopped being a single header, which is the one thing to plan around

> A C++ test framework with test names that need not be identifiers, expression-based assertions and local sections, now shipped as an ordinary multi-header library.

**catchorg/Catch2** — A modern, C++-native, test framework for unit-tests, TDD and BDD - using C++14, C++17 and later (C++11 support is in v2.x branch, and C++03 on the Catch1.x branch)

- Repository: https://github.com/catchorg/Catch2
- Website: https://discord.gg/4CWS9zD
- Stars: 21,524 · Forks: 3,489
- Language: C++
- License: BSL-1.0
- Published: 2026-10-06 · Updated: 2026-10-06 · Language: en
- Canonical page: https://hysenlabs.com/projects/catchorg-catch2

## Test names are sentences, assertions are expressions

The README's stated advantage is that using Catch2 is simple and natural, and it names three specifics: test names do not have to be valid identifiers, assertions look like normal C++ boolean expressions, and sections share setup and teardown locally.

The first point is worth expanding because it changes what a test suite looks like. Here is the README's factorial example in full:

```cpp
#include <catch2/catch_test_macros.hpp>

#include <cstdint>

uint32_t factorial( uint32_t number ) {
    return number <= 1 ? number : factorial(number-1) * number;
}

TEST_CASE( "Factorials are computed", "[factorial]" ) {
    REQUIRE( factorial( 1) == 1 );
    REQUIRE( factorial( 2) == 2 );
    REQUIRE( factorial( 3) == 6 );
    REQUIRE( factorial(10) == 3'628'800 );
}
```

`REQUIRE( factorial(10) == 3'628'800 )` is the assertion form: no macro wrapping the comparison, no separate expected and actual arguments. The macro receives an expression, so the failure message can show both sides of the comparison, which is what a debugger-less test run needs.

## Micro-benchmarking inside the same binary

Catch2 is mainly a unit testing framework, but the README also lists basic micro-benchmarking and simple BDD macros as features it provides. The benchmark example interleaves correctness checks with timing, so a benchmark that measures the wrong function is caught in the same run:

```cpp
#include <catch2/catch_test_macros.hpp>
#include <catch2/benchmark/catch_benchmark.hpp>

#include <cstdint>

uint64_t fibonacci(uint64_t number) {
    return number < 2 ? number : fibonacci(number - 1) + fibonacci(number - 2);
}

TEST_CASE("Benchmark Fibonacci", "[!benchmark]") {
    REQUIRE(fibonacci(5) == 5);

    REQUIRE(fibonacci(20) == 6'765');
    BENCHMARK("fibonacci 20") {
        return fibonacci(20);
    };
}
```

Benchmarks do not run by default. They are behind the `[!benchmark]` tag and have to be requested explicitly, which is the right default for a test binary that also runs in CI.

The header paths in these examples, `catch2/catch_test_macros.hpp` and `catch2/benchmark/catch_benchmark.hpp`, are the v3 layout. They are worth noticing because they differ from what a v2 user has in their project today.

## What the move to a normal library costs you

The README states the v3 change plainly: Catch2 is no longer a single-header library, and now behaves as a normal library with multiple headers and a separately compiled implementation.

That is a packaging change with real consequences. In v2 you could drop one header into a project and have a working test framework, which made Catch2 attractive for a quick check or a small codebase with no build system. In v3 you link against a library, so you need CMake integration or equivalent in your build. What you get back is a normal dependency: separate compilation means the implementation is compiled once rather than in every translation unit that uses it, which is where the build-time win comes from.

For an existing v2 project the README points at a migration document, `docs/migrate-v2-to-v3.md`, described as simple guidelines for getting started that collect the most common migration problems. The previous major version lives on a separate `v2.x` branch if you need to stay put.

One caveat the README states: the documentation update is slow and still ongoing. That is the honest state of the v3 documentation today, and it is worth knowing before you commit a team to the migration.

## The docs are three files, and the migration guide is the one to read first

The README points at three documents: `docs/why-catch.md` for the argument on why another C++ test framework is worth it, `docs/tutorial.md` as the getting-started guide, and `docs/Readme.md` as the reference section with all the details. The tutorial and the reference are the ones you will actually keep open.

Around those there are two more links that are useful for judging adoption before you install anything: `docs/opensource-users.md` for who uses Catch2 in open source software, and `docs/commercial-users.md` for commercial use. Both are maintained in the repository, so they are current rather than a claim on a marketing page.

The project is licensed BSL-1.0, the Boost Software License, so the commercial users list is about adoption rather than licensing anxiety. Discussion happens on Discord, and bugs go to the GitHub issue tracker.

The repository description gives the language standard targets: C++14, C++17 and later, with C++11 support on the v2.x branch and C++03 on the Catch1.x branch. The topics list no-dependencies among its tags.

## Recent releases are mostly about CMake test discovery

The v3.16.0 release notes, published on 2026-08-25, are dominated by fixes to `catch_discover_tests`, the CMake helper that registers each `TEST_CASE` with CTest individually. There were three separate fixes: a `<target>_TESTS` variable accumulating JSON fragments alongside test names, a missing escape of test names that let CMake misparse them so a test name containing a semicolon became several partial names, and `TEST_PREFIX` and `TEST_SUFFIX` arguments having leading and trailing whitespace stripped.

The second of those is instructive. The notes say that bug has existed since the first version of the script, which means the failure mode is not recent regression but a long-standing edge case in how names were escaped.

v3.15.3, from 2026-07-26, went the other way and made discovery much faster. The notes say `catch_discover_tests` registers tests in deterministic alphabetical order, that the script was rewritten to be around four to five times faster, and that registering 500 tests now takes roughly 350ms instead of 1.1 seconds. The same release made the JSON reporter's number handling locale-independent, which is the kind of fix that only shows up for someone whose locale uses a comma as the decimal separator.

So the recent work is in the integration layer rather than the test API. If you are evaluating Catch2, that is where the effort has been going.

## Building across platforms from the CI configuration

The badges at the top of the README are a quick map of what is tested on every change: separate Linux workflows for simple and other builds, a macOS workflow, and AppVeyor for the Windows side. There is also a coverage badge wired to Codecov against the `devel` branch, which is the branch the v3 work happens on.

The `devel` branch distinction is worth holding on to when reading releases. The README says you are on `devel` where v3 is developed, and that the v2 line lives on its own branch. Release tags such as v3.16.0 come off that development line.

There is also a link to compiling a Catch2 example on Godbolt through the `try-online` badge, which is the fastest way to check that your compiler and standard version are compatible with the header layout before you wire it into a build.

## Conclusion

Catch2's design has been stable for a long time and the reason people stay is visible in one short example: a test name that is a readable sentence, assertions that are plain boolean expressions, and sections that scope setup to the part of the test that needs it. What changed recently is packaging rather than design, since v3 turned the framework from one header you paste into a project into a library with multiple headers and a separately compiled implementation. That trade buys proper CMake integration and faster builds, and it costs you a `find_package` and a migration step from v2. The v3.16.0 notes are dominated by fixes to CMake test discovery rather than new capability, which tells you where the remaining work is. Licence is BSL-1.0, and the last push was on 2026-09-14.

## FAQ

### What is Catch2?

Catch2 is a C++-native test framework for unit tests, TDD and BDD, with some micro-benchmarking and BDD macros on top. Its distinguishing features per the README are test names that need not be valid identifiers, assertions written as ordinary boolean expressions, and sections that share setup and teardown locally. It targets C++14 and later, with older standards supported on separate version branches.

### Is Catch2 still a header-only library?

No, and this is the main change in v3. Catch2 used to ship as a single header you could drop into a project. The README states that v3 makes it a normal library with multiple headers and a separately compiled implementation. The upside is that the implementation is compiled once instead of in every translation unit, and the project now integrates with CMake. The cost is that you need build system integration rather than a single file.

### How do I use Catch2 with CMake?

Link against the Catch2 library from your CMake build, include the v3 headers such as `catch2/catch_test_macros.hpp` for the macros and `catch2/benchmark/catch_benchmark.hpp` for benchmarks, and define a test executable containing your `TEST_CASE` blocks. To register each test individually with CTest, use the `catch_discover_tests` helper, which is what the recent release notes have been fixing and speeding up. Tests tagged `[!benchmark]` do not run unless you ask for them explicitly.

### How do I install Catch2 on Ubuntu?

Catch2 is built and installed from source, unlike the single-header v2 layout. Clone the repository, check out the v3 branch if you want the current major version, then use CMake to configure and build it and install it to your prefix. After that, point your project's CMake at it with `find_package`. The distribution packages available on Linux can lag behind the releases, and the repository documentation is the current reference.

### How do I set up Catch2 for a new project?

Install or build the v3 library, write a test executable that includes `catch2/catch_test_macros.hpp`, declare your cases with `TEST_CASE` and assert with `REQUIRE` or `CHECK`, then add the Catch2 CMake package and `catch_discover_tests` to register each case with CTest. If you are coming from v2, read `docs/migrate-v2-to-v3.md` in the repository first, because the include paths changed and the single-header distribution is gone.

## Sources

- [catchorg/Catch2 on GitHub](https://github.com/catchorg/Catch2)
- [License: BSL-1.0](https://github.com/catchorg/Catch2/blob/devel/LICENSE)
- [Project website](https://discord.gg/4CWS9zD)
- [README](https://github.com/catchorg/Catch2/blob/devel/README.md)
- [Releases](https://github.com/catchorg/Catch2/releases)

---

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