# 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> 。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://chenxiaowei.gitbook.io/cmake-cookbook/4.0-chinese/4.2-chinese.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
