From c025a85914eb118533dd1a4fd1e4c0884ca0bc31 Mon Sep 17 00:00:00 2001
From: wuyangfan <1102042793@qq.com>
Date: Mon, 18 May 2026 00:05:05 +0800
Subject: [PATCH] docs: add guide for library tests across translation units
Document single-executable linking, CMake layout, and custom main.
Adds FAQ entry and cross-links from cmake-integration.
Fixes #421
---
docs/Readme.md | 1 +
docs/cmake-integration.md | 4 ++
docs/faq.md | 12 +++++
docs/library-tests.md | 96 +++++++++++++++++++++++++++++++++++++++
4 files changed, 113 insertions(+)
create mode 100644 docs/library-tests.md
diff --git a/docs/Readme.md b/docs/Readme.md
index fb54f91711..818f3d855a 100644
--- a/docs/Readme.md
+++ b/docs/Readme.md
@@ -20,6 +20,7 @@ Once you're up and running consider the following reference material.
**Fine tuning:**
* [Supplying your own main()](own-main.md#top)
+* [Tests inside a library project](library-tests.md#top)
* [Compile-time configuration](configuration.md#top)
* [String Conversions](tostring.md#top)
diff --git a/docs/cmake-integration.md b/docs/cmake-integration.md
index e63e67f61b..e21b4df490 100644
--- a/docs/cmake-integration.md
+++ b/docs/cmake-integration.md
@@ -13,6 +13,10 @@
Because we use CMake to build Catch2, we also provide a couple of
integration points for our users.
+If you ship tests with a library across multiple `.cpp` files, see
+[Tests inside a library project](library-tests.md#top) so every
+`TEST_CASE` is linked into the same test executable.
+
1) Catch2 exports a (namespaced) CMake target
2) Catch2's repository contains CMake scripts for automatic registration
of `TEST_CASE`s in CTest
diff --git a/docs/faq.md b/docs/faq.md
index 80923d26e8..422cc61b2f 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -7,6 +7,7 @@
[Why cannot I derive from the built-in reporters?](#why-cannot-i-derive-from-the-built-in-reporters)
[What is Catch2's ABI stability policy?](#what-is-catch2s-abi-stability-policy)
[What is Catch2's API stability policy?](#what-is-catch2s-api-stability-policy)
+[How do I run tests that live in my library's source tree?](#how-do-i-run-tests-that-live-in-my-librarys-source-tree)
[Does Catch2 support running tests in parallel?](#does-catch2-support-running-tests-in-parallel)
[Can I compile Catch2 into a dynamic library?](#can-i-compile-catch2-into-a-dynamic-library)
[What repeatability guarantees does Catch2 provide?](#what-repeatability-guarantees-does-catch2-provide)
@@ -51,6 +52,17 @@ This means that we will not knowingly make backwards-incompatible changes
without incrementing the major version number.
+## How do I run tests that live in my library's source tree?
+
+Put every translation unit that defines `TEST_CASE`s into **one** test
+executable and link it with `Catch2::Catch2WithMain` (or a single custom
+`main` plus `Catch2::Catch2`). Do not rely on tests compiled only into a
+static library unless you know they are pulled into the runner's link.
+
+See [Tests inside a library project](library-tests.md#top) for a CMake
+example and common pitfalls.
+
+
## Does Catch2 support running tests in parallel?
Not natively, no. We see running tests in parallel as the job of an
diff --git a/docs/library-tests.md b/docs/library-tests.md
new file mode 100644
index 0000000000..c579a952bd
--- /dev/null
+++ b/docs/library-tests.md
@@ -0,0 +1,96 @@
+
+# Tests inside a library project
+
+Catch2 discovers `TEST_CASE`s at static initialization time in every
+translation unit that is **linked into the test executable**. If you
+ship tests with a library (several `.cpp` files, each with its own
+`TEST_CASE`s), they will not run unless all of those object files end up
+in the same binary you execute.
+
+This page summarizes the layout that avoids “missing” tests when using
+multiple compilation units. It replaces older v2 guidance around
+`CATCH_CONFIG_RUNNER`; in v3 use [`Catch::Session`](own-main.md#top)
+instead (see also [Migrating from v2 to v3](migrate-v2-to-v3.md#top)).
+
+
+## Recommended layout
+
+```
+mylib/
+ include/
+ src/
+ tests/
+ test_foo.cpp
+ test_bar.cpp
+ CMakeLists.txt
+```
+
+Keep test sources under `tests/` (or similar) and link them into **one**
+test executable together with your library.
+
+
+## CMake (recommended)
+
+```cmake
+add_library(mylib ...)
+
+file(GLOB MYLIB_TEST_SOURCES CONFIGURE_DEPENDS tests/*.cpp)
+# If you provide a custom main, exclude it from the glob and add it explicitly.
+list(FILTER MYLIB_TEST_SOURCES EXCLUDE REGEX "test_main\\.cpp$")
+
+add_executable(mylib-tests ${MYLIB_TEST_SOURCES})
+target_link_libraries(mylib-tests PRIVATE mylib Catch2::Catch2WithMain)
+
+include(CTest)
+include(Catch)
+catch_discover_tests(mylib-tests)
+```
+
+`Catch2::Catch2WithMain` supplies a single `main` for the executable.
+Register tests with [`catch_discover_tests`](cmake-integration.md#catch_discover_tests)
+so CTest sees each `TEST_CASE`.
+
+**Common mistake:** compiling test `.cpp` files into the `mylib` static
+library but linking only `mylib` into a tiny runner that has no test
+sources of its own. The test object files are then not linked into the
+runner and Catch2 never sees them. Prefer linking test sources directly
+into `mylib-tests`, or ensure the static library is linked with flags
+that pull in all object files (for example `--whole-archive` on GCC/Clang).
+
+
+## Custom `main`
+
+If you need code to run before or after the test session, provide your
+own `main` and link only `Catch2::Catch2`:
+
+```cpp
+#include
+
+int main(int argc, char* argv[]) {
+ // Optional library-wide setup
+ const int result = Catch::Session().run(argc, argv);
+ // Optional library-wide teardown
+ return result;
+}
+```
+
+There must be exactly **one** `main` in the test executable. See
+[Supplying main() yourself](own-main.md#top) for more recipes.
+
+
+## Without CMake
+
+Compile every `.cpp` that defines `TEST_CASE`s into the same final
+executable. If tests disappear after a refactor, check that each test
+translation unit is still on the link line of the binary you run.
+
+
+## Further reading
+
+- [Getting Catch to work across multiple compilation units](https://groups.google.com/g/catch-forum/c/pQchmF6Pr0E) (Catch forum)
+- [CATCH_CONFIG_RUNNER usage](https://groups.google.com/g/catch-forum/c/FV0Qo62DvgY) (Catch2 v2; use `Catch::Session` in v3)
+
+
+---
+
+[Home](Readme.md#top)