How to Migrate from Catch2 v2 to v3: Single-Header to Multi-Header Guide
To migrate from Catch2 v2 to v3, replace the monolithic <catch2/catch.hpp> include with <catch2/catch_all.hpp> or fine-grained headers like <catch2/catch_test_macros.hpp>, link against the Catch2::Catch2 CMake target instead of using header-only compilation, and update matcher namespaces to Catch::Matchers.
Catch2 v3 introduces a fundamental architectural change in the catchorg/Catch2 repository, moving away from the single-header distribution model toward a modular multi-header structure compiled as a static library. This shift reduces compilation times by approximately 80% but requires updates to your build system configuration, include directives, and in some cases, minor code adjustments for breaking changes.
Key Architectural Changes in Catch2 v3
The most significant difference between v2 and v3 is the header organization and build model. According to the Catch2 source code, the monolithic <catch2/catch.hpp> has been replaced by a focused header structure centered around src/catch2/catch_all.hpp as the umbrella header.
Static library compilation is now the default distribution method. Rather than compiling Catch2's implementation into every translation unit that includes the header (header-only mode), you now link against pre-compiled static library targets: Catch2 (library only) or Catch2WithMain (library including the default main() function). This change, implemented in the CMake configuration at the repository root, dramatically improves build performance by eliminating redundant parsing and compilation of template-heavy code.
C++14 minimum requirement is now enforced throughout the codebase. All code in src/catch2/ assumes at least C++14 support, requiring compiler upgrades for projects previously targeting C++11.
Migration Strategies
You have two approaches when migrating from the v2 single-header model to v3.
Option 1: Amalgamated Files (Transition Path)
For projects that need minimal build system changes, Catch2 v3 provides amalgamated files in the extras/ directory. These files combine the new multi-header structure into two files: extras/catch_amalgamated.hpp and extras/catch_amalgamated.cpp.
Copy these files into your project and replace your v2 include:
// Old v2 single-header
#include <catch2/catch.hpp>
// New v3 amalgamated
#include "catch_amalgamated.hpp"
This approach works immediately but sacrifices the compile-time benefits of the static library model, as it reverts to header-only compilation with longer build times.
Option 2: Full Static Library Integration (Recommended)
The recommended approach involves consuming Catch2 as a compiled static library. This requires updating your CMake configuration and include statements to leverage the modular headers in src/catch2/, allowing the compiler to process only the declarations your test files actually use.
Step-by-Step CMake Migration
Update your CMakeLists.txt to use the exported targets instead of header-only includes.
# Catch2 v2 (header-only)
add_executable(tests test.cpp)
target_link_libraries(tests PRIVATE catch2)
# Catch2 v3 (static library)
find_package(Catch2 CONFIG REQUIRED)
add_executable(tests test.cpp)
target_link_libraries(tests PRIVATE Catch2::Catch2)
# OR use Catch2::Catch2WithMain to have Catch2 provide main()
If you use Catch2::Catch2WithMain, remove any translation unit that defines CATCH_CONFIG_MAIN or CATCH_CONFIG_RUNNER. The library now provides the entry point automatically through the linked archive.
Updating Header Includes
Replace the single v2 header with either the v3 umbrella header or selective includes for finer build performance.
Using the umbrella header:
// Replace this v2 include
#include <catch2/catch.hpp>
// With this v3 include
#include <catch2/catch_all.hpp>
Using fine-grained headers (recommended):
#include <catch2/catch_test_macros.hpp> // Core macros: TEST_CASE, SECTION, REQUIRE
#include <catch2/matchers/catch_matchers_all.hpp> // Matchers interface
#include <catch2/generators/catch_generators_all.hpp> // Data generators
Selective inclusion from src/catch2/ allows the compiler to process only the declarations your test files actually need, further reducing build times compared to the umbrella header approach.
Handling Breaking Changes in Catch2 v3
Several interfaces changed between v2 and v3 that require code updates beyond include paths.
Matcher namespace restructuring: All matchers now reside in the Catch::Matchers namespace, and some names changed to be more specific. For example, the string matcher Contains is now ContainsSubstring. Update your code accordingly:
// v2 syntax
REQUIRE_THAT(str, Catch::Contains("substring"));
// v3 syntax
#include <catch2/matchers/catch_matchers_all.hpp>
REQUIRE_THAT(str, Catch::Matchers::ContainsSubstring("substring"));
Custom reporters and listeners: If you implemented custom reporters, note that the interfaces have changed significantly in v3. Consult the docs/reporters.md and docs/reporter-events.md files in the repository for updated base class signatures and event handling.
Complete Migration Example
Here is a complete before-and-after comparison for a typical test file.
Catch2 v2 (test.cpp):
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
TEST_CASE("Vector contains element") {
std::vector<int> v{1, 2, 3};
REQUIRE_THAT(v, Catch::Contains(2));
}
Catch2 v3 (test.cpp):
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_all.hpp>
using Catch::Matchers::Contains;
TEST_CASE("Vector contains element") {
std::vector<int> v{1, 2, 3};
REQUIRE_THAT(v, Contains(2));
}
CMakeLists.txt (v3):
cmake_minimum_required(VERSION 3.16)
project(MyTests)
find_package(Catch2 CONFIG REQUIRED)
add_executable(tests test.cpp)
target_link_libraries(tests PRIVATE Catch2::Catch2WithMain)
Summary
- Replace
<catch2/catch.hpp>with<catch2/catch_all.hpp>or granular headers fromsrc/catch2/ - Link against
Catch2::Catch2orCatch2::Catch2WithMainCMake targets instead of header-only usage - Remove custom
main()definitions when usingCatch2::Catch2WithMain - Update matcher namespaces to
Catch::Matchersand replace deprecated names likeContainswithContainsSubstringfor string matching - Require C++14 minimum for all compilation units consuming Catch2 headers
Frequently Asked Questions
Do I have to use CMake to migrate to Catch2 v3?
No, CMake is not strictly required, though it is the primary supported build system for the catchorg/Catch2 repository. You can compile the source files in src/catch2/ manually into a static library using your preferred build system and link it to your test executables. However, using CMake with the exported Catch2::Catch2 target handles include directories and transitive dependencies automatically, making it the recommended approach.
What happened to CATCH_CONFIG_MAIN in Catch2 v3?
CATCH_CONFIG_MAIN is no longer necessary if you link against the Catch2::Catch2WithMain target, which provides its own main() function implementation in the static library. If you define your own main() function for custom setup, link against Catch2::Catch2 instead. If you use the amalgamated header approach, you still define CATCH_CONFIG_MAIN in exactly one translation unit as in v2.
How do I fix matcher compilation errors after migrating?
Matcher-related errors usually indicate namespace issues or renamed interfaces. In v3, all matchers moved to the Catch::Matchers namespace, and some specific matchers were renamed (e.g., Contains became ContainsSubstring for string matching). Include <catch2/matchers/catch_matchers_all.hpp> from src/catch2/ and qualify matcher names with Catch::Matchers:: or use specific using declarations to resolve these errors.
Can I still use Catch2 v3 as a single-header library?
Yes, through the amalgamated files provided in extras/catch_amalgamated.hpp and extras/catch_amalgamated.cpp. Copy both files to your project, include the header, and compile the .cpp file alongside your tests. However, this approach compiles Catch2's implementation into every translation unit that includes the header, negating the approximately 80% compile-time improvement offered by the static library model in src/catch2/.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →