# 4.3 使用Google Test库进行单元测试

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

本示例中，我们将演示如何在CMake的帮助下使用Google Test框架实现单元测试。与前一个配置相比，Google Test框架不仅仅是一个头文件，也是一个库，包含两个需要构建和链接的文件。可以将它们与我们的代码项目放在一起，但是为了使代码项目更加轻量级，我们将选择在配置时，下载一个定义良好的Google Test，然后构建框架并链接它。我们将使用较新的`FetchContent`模块(从CMake版本3.11开始可用)。第8章中会继续讨论`FetchContent`，在这里将讨论模块在底层是如何工作的，并且还将演示如何使用`ExternalProject_Add`进行模拟。此示例的灵感来自(改编自) <https://cmake.org/cmake/help/v3.11/module/FetchContent.html> 示例。

## 准备工作

`main.cpp`、`sum_integers.cpp`和`sum_integers.hpp`与之前相同，修改`test.cpp`:

```cpp
#include "sum_integers.hpp"
#include "gtest/gtest.h"

#include <vector>

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

TEST(example, sum_zero) {
  auto integers = {1, -1, 2, -2, 3, -3};
  auto result = sum_integers(integers);
  ASSERT_EQ(result, 0);
}

TEST(example, sum_five) {
  auto integers = {1, 2, 3, 4, 5};
  auto result = sum_integers(integers);
  ASSERT_EQ(result, 15);
}
```

如上面的代码所示，我们显式地将`gtest.h`，而不将其他Google Test源放在代码项目存储库中，会在配置时使用`FetchContent`模块下载它们。

## 具体实施

下面的步骤描述了如何设置`CMakeLists.txt`，使用GTest编译可执行文件及其相应的测试:

1. 与前两个示例相比，`CMakeLists.txt`的开头基本没有变化，CMake 3.11才能使用`FetchContent`模块:

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

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

   # require C++11
   set(CMAKE_CXX_STANDARD 11)
   set(CMAKE_CXX_EXTENSIONS OFF)
   set(CMAKE_CXX_STANDARD_REQUIRED ON)
   set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS 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)
   ```
2. 然后引入一个`if`，检查`ENABLE_UNIT_TESTS`。默认情况下，它为`ON`，但有时需要设置为`OFF`，以免在没有网络连接时，也能使用Google Test:

   ```
   option(ENABLE_UNIT_TESTS "Enable unit tests" ON)
   message(STATUS "Enable testing: ${ENABLE_UNIT_TESTS}")

   if(ENABLE_UNIT_TESTS)
       # all the remaining CMake code will be placed here
   endif()
   ```
3. `if`内部包含`FetchContent`模块，声明要获取的新内容，并查询其属性:

   ```
   include(FetchContent)

   FetchContent_Declare(
     googletest
     GIT_REPOSITORY https://github.com/google/googletest.git
     GIT_TAG release-1.8.0
   )

   FetchContent_GetProperties(googletest)
   ```
4. 如果内容还没有获取到，将尝试获取并配置它。这需要添加几个可以链接的目标。本例中，我们对`gtest_main`感兴趣。该示例还包含一些变通方法，用于使用在Visual Studio下的编译:

   ```
   if(NOT googletest_POPULATED)
     FetchContent_Populate(googletest)

     # Prevent GoogleTest from overriding our compiler/linker options
     # when building with Visual Studio
     set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
     # Prevent GoogleTest from using PThreads
     set(gtest_disable_pthreads ON CACHE BOOL "" FORCE)

     # adds the targers: gtest, gtest_main, gmock, gmock_main
     add_subdirectory(
       ${googletest_SOURCE_DIR}
       ${googletest_BINARY_DIR}
       )

     # Silence std::tr1 warning on MSVC
     if(MSVC)
       foreach(_tgt gtest gtest_main gmock gmock_main)
         target_compile_definitions(${_tgt}
           PRIVATE
               "_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING"
         )
       endforeach()
     endif()
   endif()
   ```
5. 然后，使用`target_sources`和`target_link_libraries`命令，定义`cpp_test`可执行目标并指定它的源文件:

   ```
   add_executable(cpp_test "")

   target_sources(cpp_test
     PRIVATE
         test.cpp
     )

   target_link_libraries(cpp_test
     PRIVATE
       sum_integers
       gtest_main
     )
   ```
6. 最后，使用`enable_test`和`add_test`命令来定义单元测试:

   ```
   enable_testing()

   add_test(
     NAME google_test
     COMMAND $<TARGET_FILE:cpp_test>
     )
   ```
7. 现在，准备配置、构建和测试项目:

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

   Test project /home/user/cmake-cookbook/chapter-04/recipe-03/cxx-example/build
       Start 1: google_test
   1/1 Test #1: google_test ...................... Passed 0.00 sec

   100% tests passed, 0 tests failed out of 1

   Total Test time (real) = 0.00 sec
   ```
8. 可以直接运行`cpp_test`:

   ```
   $ ./cpp_test

   [==========] Running 2 tests from 1 test case.
   [----------] Global test environment set-up.
   [----------] 2 tests from example
   [ RUN ] example.sum_zero
   [ OK ] example.sum_zero (0 ms)
   [ RUN ] example.sum_five
   [ OK ] example.sum_five (0 ms)
   [----------] 2 tests from example (0 ms total)

   [----------] Global test environment tear-down
   [==========] 2 tests from 1 test case ran. (0 ms total)
   [ PASSED ] 2 tests.
   ```

## 工作原理

`FetchContent`模块支持通过`ExternalProject`模块，在配置时填充内容，并在其3.11版本中成为CMake的标准部分。而`ExternalProject_Add()`在构建时(见第8章)进行下载操作，这样`FetchContent`模块使得构建可以立即进行，这样获取的主要项目和外部项目(在本例中为Google Test)仅在第一次执行CMake时调用，使用`add_subdirectory`可以嵌套。

为了获取Google Test，首先声明外部内容:

```
include(FetchContent)

FetchContent_Declare(
    googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG release-1.8.0
)
```

本例中，我们获取了一个带有特定标记的Git库(release-1.8.0)，但是我们也可以从Subversion、Mercurial或HTTP(S)源获取一个外部项目。有关可用选项，可参考相应的`ExternalProject_Add`命令的选项，网址是<https://cmake.org/cmake/help/v3.11/module/ExternalProject.html> 。

调用`FetchContent_Populate()`之前，检查是否已经使用`FetchContent_GetProperties()`命令处理了内容填充；否则，调用`FetchContent_Populate()`超过一次后，就会抛出错误。

`FetchContent_Populate(googletest)`用于填充源并定义`googletest_SOURCE_DIR`和`googletest_BINARY_DIR`，可以使用它们来处理Google Test项目(使用`add_subdirectory()`，因为它恰好也是一个CMake项目):

```
add_subdirectory(
  ${googletest_SOURCE_DIR}
  ${googletest_BINARY_DIR}
  )
```

前面定义了以下目标：`gtest`、`gtest_main`、`gmock`和`gmock_main`。这个配置中，作为单元测试示例的库依赖项，我们只对`gtest_main`目标感兴趣：

```
target_link_libraries(cpp_test
  PRIVATE
    sum_integers
    gtest_main
)
```

构建代码时，可以看到如何正确地对Google Test进行配置和构建。有时，我们希望升级到更新的Google Test版本，这时需要更改的唯一一行就是详细说明`GIT_TAG`的那一行。

## 更多信息

了解了`FetchContent`及其构建时的近亲`ExternalProject_Add`，我们将在第8章中重新讨论这些命令。有关可用选项的详细讨论，可参考<https://cmake.org/cmake/help/v3.11/module/FetchContent.html> 。

本示例中，我们在配置时获取源代码，也可以将它们安装在系统环境中，并使用`FindGTest`模块来检测库和头文件(<https://cmake.org/cmake/help/v3.5/module/FindTest.html> )。从3.9版开始，CMake还提供了一个Google Test模块(<https://cmake.org/cmake/help/v3.9/module/GoogleTest.html> )，它提供了一个`gtest_add_tests`函数。通过搜索Google Test宏的源代码，可以使用此函数自动添加测试。

当然，Google Test有许多有趣的的特性，可在 <https://github.com/google/googletest> 查看。


---

# 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.3-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.
