ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 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`: ```c++ #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`模块: ```cmake # 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: ```cmake 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`模块,声明要获取的新内容,并查询其属性: ```cmake 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下的编译: ```cmake 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`可执行目标并指定它的源文件: ```cmake 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`命令来定义单元测试: ```cmake enable_testing() add_test( NAME google_test COMMAND $<TARGET_FILE:cpp_test> ) ``` 7. 现在,准备配置、构建和测试项目: ```shell $ 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`: ```shell $ ./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,首先声明外部内容: ```cmake 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项目): ```cmake add_subdirectory( ${googletest_SOURCE_DIR} ${googletest_BINARY_DIR} ) ``` 前面定义了以下目标:`gtest`、`gtest_main`、`gmock`和`gmock_main`。这个配置中,作为单元测试示例的库依赖项,我们只对`gtest_main`目标感兴趣: ```cmake 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 查看。