> For the complete documentation index, see [llms.txt](https://chenxiaowei.gitbook.io/cmake-cookbook/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://chenxiaowei.gitbook.io/cmake-cookbook/4.0-chinese/4.2-chinese.md).

# 4.2 使用Catch2库进行单元测试

**NOTE**:*此示例代码可以在* <https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-04/recipe-02> *中找到，包含一个C++的示例。该示例在CMake 3.5版(或更高版本)中是有效的，并且已经在GNU/Linux、macOS和Windows上进行过测试。*

前面的配置中，使用返回码来表示`test.cpp`测试的成功或失败。对于简单功能没问题，但是通常情况下，我们想要使用一个测试框架，它提供了相关基础设施来运行更复杂的测试，包括固定方式进行测试，与数值公差的比较，以及在测试失败时输出更好的错误报告。这里，我们用目前比较流行的测试库Catch2( <https://github.com/catchorg/Catch2> )来进行演示。这个测试框架有个很好的特性，它可以通过单个头库包含在项目中进行测试，这使得编译和更新框架特别容易。这个配置中，我们将CMake和Catch2结合使用，来测试上一个求和代码。

我们需要`catch.hpp`头文件，可以从 <https://github.com/catchorg/Catch2> (我们使用的是版本2.0.1)下载，并将它与`test.cpp`一起放在项目的根目录下。

## 准备工作

`main.cpp`、`sum_integers.cpp`和`sum_integers.hpp`与之前的示例相同，但将更新`test.cpp`:

```cpp
#include "sum_integers.hpp"

// this tells catch to provide a main()
// only do this in one cpp file
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include <vector>

TEST_CASE("Sum of integers for a short vector", "[short]")
{
  auto integers = {1, 2, 3, 4, 5};
  REQUIRE(sum_integers(integers) == 15);
}

TEST_CASE("Sum of integers for a longer vector", "[long]")
{
  std::vector<int> integers;
  for (int i = 1; i < 1001; ++i)
  {
    integers.push_back(i);
  }
  REQUIRE(sum_integers(integers) == 500500);
}
```

`catch.hpp`头文件可以从<https://github.com/catchorg/Catch2> (版本为2.0.1)下载，并将它与`test.cpp`放在项目的根目录中。

## 具体实施

使用Catch2库，需要修改之前的所使用`CMakeList.txt`：

1. 保持`CMakeLists.txt`大多数部分内容不变:

   ```
   # set minimum cmake version
   cmake_minimum_required(VERSION 3.5 FATAL_ERROR)

   # project name and language
   project(recipe-02 LANGUAGES CXX)

   # require C++11
   set(CMAKE_CXX_STANDARD 11)
   set(CMAKE_CXX_EXTENSIONS OFF)
   set(CMAKE_CXX_STANDARD_REQUIRED ON)

   # example library
   add_library(sum_integers sum_integers.cpp)

   # main code
   add_executable(sum_up main.cpp)
   target_link_libraries(sum_up sum_integers)

   # testing binary
   add_executable(cpp_test test.cpp)
   target_link_libraries(cpp_test sum_integers)
   ```
2. 对于上一个示例的配置，需要保留一个测试，并重命名它。注意，`--success`选项可传递给单元测试的可执行文件。这是一个Catch2选项，测试成功时，也会有输出:

   ```
   enable_testing()

   add_test(
     NAME catch_test
     COMMAND $<TARGET_FILE:cpp_test> --success
     )
   ```
3. 就是这样！让我们来配置、构建和测试。CTest中，使用`-V`选项运行测试，以获得单元测试可执行文件的输出:

   ```
   $ mkdir -p build
   $ cd build
   $ cmake ..
   $ cmake --build .
   $ ctest -V

   UpdateCTestConfiguration from :/home/user/cmake-cookbook/chapter-04/recipe-02/cxx-example/build/DartConfiguration.tcl
   UpdateCTestConfiguration from :/home/user/cmake-cookbook/chapter-04/recipe-02/cxx-example/build/DartConfiguration.tcl
   Test project /home/user/cmake-cookbook/chapter-04/recipe-02/cxx-example/build
   Constructing a list of tests
   Done constructing a list of tests
   Updating test list for fixtures
   Added 0 tests to meet fixture requirements
   Checking test dependency graph...
   Checking test dependency graph end
   test 1
   Start 1: catch_test
   1: Test command: /home/user/cmake-cookbook/chapter-04/recipe-02/cxx-example/build/cpp_test "--success"
   1: Test timeout computed to be: 10000000
   1:
   1: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   1: cpp_test is a Catch v2.0.1 host application.
   1: Run with -? for options
   1:
   1: ----------------------------------------------------------------
   1: Sum of integers for a short vector
   1: ----------------------------------------------------------------
   1: /home/user/cmake-cookbook/chapter-04/recipe-02/cxx-example/test.cpp:10
   1: ...................................................................
   1:
   1: /home/user/cmake-cookbook/chapter-04/recipe-02/cxx-example/test.cpp:12:
   1: PASSED:
   1: REQUIRE( sum_integers(integers) == 15 )
   1: with expansion:
   1: 15 == 15
   1:
   1: ----------------------------------------------------------------
   1: Sum of integers for a longer vector
   1: ----------------------------------------------------------------
   1: /home/user/cmake-cookbook/chapter-04/recipe-02/cxx-example/test.cpp:15
   1: ...................................................................
   1:
   1: /home/user/cmake-cookbook/chapter-04/recipe-02/cxx-example/test.cpp:20:
   1: PASSED:
   1: REQUIRE( sum_integers(integers) == 500500 )
   1: with expansion:
   1: 500500 (0x7a314) == 500500 (0x7a314)
   1:
   1: ===================================================================
   1: All tests passed (2 assertions in 2 test cases)
   1:
   1/1 Test #1: catch_test ....................... Passed 0.00 s

   100% tests passed, 0 tests failed out of 1

   Total Test time (real) = 0.00 se
   ```
4. 我们也可以测试`cpp_test`的二进制文件，可以直接从Catch2中看到输出:

   ```
   $ ./cpp_test --success

   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   cpp_test is a Catch v2.0.1 host application.
   Run with -? for options
   -------------------------------------------------------------------
   Sum of integers for a short vector
   -------------------------------------------------------------------
   /home/user/cmake-cookbook/chapter-04/recipe-02/cxx-example/test.cpp:10
   ...................................................................
   /home/user/cmake-cookbook/chapter-04/recipe-02/cxx-example/test.cpp:12:
   PASSED:
   REQUIRE( sum_integers(integers) == 15 )
   with expansion:
   15 == 15
   -------------------------------------------------------------------
   Sum of integers for a longer vector
   -------------------------------------------------------------------
   /home/user/cmake-cookbook/chapter-04/recipe-02/cxx-example/test.cpp:15
   ...................................................................
   /home/user/cmake-cookbook/chapter-04/recipe-02/cxx-example/test.cpp:20:
   PASSED:
   REQUIRE( sum_integers(integers) == 500500 )
   with expansion:
   500500 (0x7a314) == 500500 (0x7a314)
   ===================================================================
   All tests passed (2 assertions in 2 test cases)
   ```
5. Catch2将生成一个可执行文件，还可以尝试执行以下命令，以探索单元测试框架提供的选项:

   ```
   $ ./cpp_test --help
   ```

## 工作原理

Catch2是一个单头文件测试框架，所以不需要定义和构建额外的目标。只需要确保CMake能找到`catch.hpp`，从而构建`test.cpp`即可。为了方便起见，将它放在与`test.cpp`相同的目录中，我们可以选择一个不同的位置，并使用`target_include_directory`指示该位置。另一种方法是将头部封装到接口库中，这可以在Catch2文档中说明( <https://github.com/catchorg/catch2/blob/maste/docs/build.systems.md#cmake> ):

```
# Prepare "Catch" library for other executables 
set(CATCH_INCLUDE_DIR
${CMAKE_CURRENT_SOURCE_DIR}/catch) 

add_library(Catch
INTERFACE) 

target_include_directories(Catch INTERFACE
${CATCH_INCLUDE_DIR})
```

然后，我们对库进行如下链接:

```
target_link_libraries(cpp_test Catch)
```

回想一下第3中的讨论，在第1章从简单的可执行库到接口库，是CMake提供的伪目标库，这些伪目标库对于指定项目外部目标的需求非常有用。

## 更多信息

这是一个简单的例子，主要关注CMake。当然，Catch2提供了更多功能。有关Catch2框架的完整文档，可访问 <https://github.com/catchorg/Catch2> 。

Catch2代码库包含有CMake函数，用于解析Catch测试并自动创建CMake测试，不需要显式地输入`add_test()`函数，可见 <https://github.com/catchorg/Catch2/blob/master/contrib/ParseAndAddCatchTests.cmake> 。
