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)