20、测试框架集成:使用FetchContent下载Google Test、Catch2、Boost.Test

说到CMake的依赖管理,测试框架这块儿我感触挺深的。早些年做项目,测试代码的依赖管理简直是个噩梦——手动下载、配置路径、版本冲突……每次换机器都要折腾半天。后来用了FetchContent,嗯,世界清净了。

今天咱们就聊聊怎么用FetchContent搞定三大主流C++测试框架:Google Test、Catch2和Boost.Test。这三种框架各有千秋,我根据项目需求选过不同的组合,下面把经验分享给你。

为什么用FetchContent管理测试框架?

你想想看,测试框架本身也是第三方库。如果用手动方式管理,无非是:

  • 把源码拷到项目里——版本更新麻烦,仓库体积膨胀
  • 用系统包管理器安装——不同机器环境不一致,CI/CD容易翻车
  • 用Git Submodule——管理起来繁琐,同事忘了更新就出问题

FetchContent的好处是:声明式依赖。你在CMakeLists.txt里写清楚要哪个版本,CMake自动下载、构建、集成。项目克隆下来,直接cmake -B build就能跑,省心。

核心思路:测试框架是开发期依赖,不应该污染系统环境。用FetchContent把它们隔离在构建目录里,干净利落。

集成Google Test

Google Test(简称GTest)是我用得最多的框架。它在C++社区里普及率极高,文档完善,断言丰富。我在几个中型项目里都用它,配合CMake的CTest,测试报告一目了然。

先看一个完整的集成示例:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(MyProject LANGUAGES CXX)

# 启用测试
enable_testing()

# 使用FetchContent下载Google Test
include(FetchContent)

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

# 设置GTest不构建示例和测试,加快编译
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
set(BUILD_GMOCK ON CACHE BOOL "" FORCE)
set(BUILD_GTEST ON CACHE BOOL "" FORCE)
set(INSTALL_GTEST OFF CACHE BOOL "" FORCE)

FetchContent_MakeAvailable(googletest)

# 添加测试可执行文件
add_executable(test_math test/test_math.cpp)
target_link_libraries(test_math GTest::gtest_main)

# 注册测试
add_test(NAME test_math COMMAND test_math)

这里有个细节:GTest::gtest_mainGTest::gtest 的区别。前者自带main函数,你只需要写测试用例;后者需要你自己提供main函数。我个人习惯用 gtest_main,省事。

小技巧:如果你用GTest的并行测试功能,记得在CMake里设置 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread"),否则链接会报错。我曾经在这个坑里卡了半小时……

集成Catch2

Catch2是另一种风格。它的特点是头文件为主,单头文件版本甚至不需要编译,直接include就能用。对于小项目或者快速原型,Catch2非常友好。

用FetchContent集成Catch2:

FetchContent_Declare(
  Catch2
  GIT_REPOSITORY https://github.com/catchorg/Catch2.git
  GIT_TAG v3.3.2
)

FetchContent_MakeAvailable(Catch2)

# Catch2 v3 需要C++17
set_target_properties(Catch2 PROPERTIES CXX_STANDARD 17)

add_executable(test_app test/test_app.cpp)
target_link_libraries(test_app Catch2::Catch2WithMain)

add_test(NAME test_app COMMAND test_app)

Catch2的测试用例写法很直观:

// test_app.cpp
#include <catch2/catch_test_macros.hpp>

TEST_CASE("加法测试", "[math]") {
    REQUIRE(1 + 1 == 2);
    REQUIRE(2 + 3 == 5);
}

TEST_CASE("字符串拼接", "[string]") {
    std::string a = "hello ";
    std::string b = "world";
    REQUIRE(a + b == "hello world");
}

我个人觉得Catch2的BDD风格(Given-When-Then)写起来很舒服,尤其是做集成测试的时候。不过要注意,Catch2 v3版本要求C++17,如果你的项目还在用C++14,得用v2版本。

注意:Catch2 v2和v3的API不兼容。如果你从v2迁移到v3,头文件路径从 catch.hpp 变成了 catch2/catch_test_macros.hpp,别搞混了。

集成Boost.Test

Boost.Test是Boost库的一部分。如果你的项目已经在用Boost,那集成Boost.Test是顺理成章的事。不过Boost库本身很大,只为了测试框架引入整个Boost有点重。我一般只在已有Boost依赖的项目里用它。

用FetchContent只下载Boost.Test部分:

FetchContent_Declare(
  Boost
  GIT_REPOSITORY https://github.com/boostorg/boost.git
  GIT_TAG boost-1.83.0
  GIT_SHALLOW TRUE
)

FetchContent_GetProperties(Boost)
if(NOT Boost_POPULATED)
  FetchContent_Populate(Boost)
  
  # 只构建test模块
  add_subdirectory(${boost_SOURCE_DIR}/libs/test ${boost_BINARY_DIR}/test)
endif()

add_executable(test_boost test/test_boost.cpp)
target_link_libraries(test_boost boost_test)

add_test(NAME test_boost COMMAND test_boost)

Boost.Test的写法比较传统:

#define BOOST_TEST_MODULE my_tests
#include <boost/test/included/unit_test.hpp>

BOOST_AUTO_TEST_SUITE(math_tests)

BOOST_AUTO_TEST_CASE(addition) {
    BOOST_CHECK(1 + 1 == 2);
    BOOST_REQUIRE(2 + 3 == 5);
}

BOOST_AUTO_TEST_SUITE_END()

Boost.Test的断言分 BOOST_CHECK(失败继续执行)和 BOOST_REQUIRE(失败立即终止),这个设计挺实用的。不过说实话,Boost.Test的编译速度比GTest和Catch2慢一些,毕竟Boost模板元编程玩得比较花。

三种框架对比

我整理了一张对比表,方便你选型:

特性 Google Test Catch2 Boost.Test
C++标准要求 C++11+ C++17 (v3) / C++11 (v2) C++11+
编译速度 中等 快(单头文件模式) 较慢
断言丰富度 ★★★★★ ★★★★ ★★★★
BDD支持 一般 优秀 一般
与CMake集成 原生支持 原生支持 需手动配置
社区活跃度 极高 中等

避坑指南

用FetchContent集成测试框架,有几个坑我踩过,给你提个醒:

  • 版本锁定要明确:别用 mainmaster 分支,指定具体tag。我曾经用 main 分支,结果某天上游更新了API,项目直接编译失败。
  • 注意GIT_SHALLOW:对于大型仓库(比如Boost),加上 GIT_SHALLOW TRUE 只拉取最新提交,能省不少下载时间。
  • 测试框架不要全局安装:用FetchContent下载的框架只在当前构建目录有效,不会污染系统。这是好事,别为了省事改成全局安装。
  • 并行编译问题:如果测试框架编译很慢,可以设置 CMAKE_BUILD_PARALLEL_LEVEL 或者用 make -j 加速。

知识体系总览

下面这张图帮你理清本章的核心逻辑:

测试框架集成:FetchContent 工作流 FetchContent Google Test Catch2 Boost.Test C++11+ 断言丰富 社区活跃 CMake原生支持 C++17 (v3) 单头文件模式 BDD支持优秀 编译速度快 C++11+ Boost生态 编译较慢 需手动配置

写在最后

测试框架的集成,说白了就是让CMake帮你搞定依赖的下载和构建。你只需要写清楚要什么、从哪里拿,剩下的交给FetchContent。我个人建议新项目优先考虑Google Test,生态好、文档全;如果团队喜欢BDD风格,Catch2也是很好的选择;Boost.Test嘛,除非项目已经在用Boost,否则没必要为了测试引入这么大一个库。

嗯,关于测试框架集成就聊到这儿。记住一点:测试代码也是代码,它的依赖管理同样重要。别因为测试框架的集成麻烦就偷懒不写测试,那才是真正的因小失大。


公众号:蓝海资料掘金营,微信deep3321