跳到正文

目录

googletest:C++ 测试的事实标准,C++17 时代的一次例行升级

googletest:C++ 测试的事实标准,C++17 时代的一次例行升级

核心判断

GoogleTest 于 2008 年开源,如今在 GitHub 上有约 3.9 万颗星(2026 年 9 月 7 日为 39495),Chromium、LLVM、Protocol Buffers、OpenCV 都把它当作测试框架,官方 README 称 Google 内部也有大量项目在使用。2026 年 8 月 10 日发布的 v1.18.0 让它重新回到 C++ 开发者的讨论视野,这次发布本身以 bug 修复为主,但它坐实了一件影响所有存量项目的事:框架已强制要求 C++17——v1.17 起生效,v1.16.x 是最后支持 C++14 的分支。官方还在 README 的 Coming Soon 里预告,接下来要引入 Abseil 依赖。

对还停在 C++11/14 的项目,这是迟早要做的升级决策;新项目则没什么可选的,它就是默认答案。

本文回答三个问题:它凭什么常青;从 C++14 到 C++17 的三次版本切换各意味着什么;新项目与存量项目各自该怎么落。

快速地图:一仓库,两套能力

这个仓库同时维护两个框架,统一版本发布:

组件职责头文件
googletest测试框架:断言、fixture、参数化、死亡测试<gtest/gtest.h>
googlemock模拟框架:mock 类、期望、匹配器、动作<gmock/gmock.h>

构建产物通常还带 _main 变体(如 GTest::gtest_main),自带 main() 省去手写。周边工具自成生态:gtest-parallel 并行跑测,TAP 监听器对接 CI,VS Code 有第三方 GoogleTest Adapter。

它是什么:断言与模拟,各管一段

官方 README 列出的能力:xUnit 架构的测试框架、自动测试发现、丰富断言、用户自定义断言、死亡测试、致命与非致命失败、值参数化与类型参数化测试、多种运行方式(单个跑、指定顺序、并行)。展开成读者关心的几条:

  • 断言体系:相等、不等、异常、浮点等断言齐全。EXPECT_* 失败只记录并继续执行当前测试,ASSERT_* 失败标记为致命(fatal)并立即从当前函数返回——后者意味着它只能用在返回 void 的函数里,这个限制后面排查一节还会出现;
  • 死亡测试(death test):验证代码在错误条件下按预期崩溃或退出,C++ 项目特有的刚需;
  • fixture:把多个测试共享的初始化、清理逻辑收进 SetUp() / TearDown()
  • 值参数化与类型参数化:一套测试逻辑跑多组输入(TEST_P)或多种类型(TYPED_TEST);
  • GoogleMock:用一套 DSL 写 mock 类与期望,解决 C++ 里手写 mock 类的样板代码问题。

xUnit 本身不是新东西,JUnit、PyUnit 用的同一套骨架。GoogleTest 做到的是在 C++ 这种没有运行时反射的语言里,用宏展开把注册、发现、报告全部自动化,同时保持零依赖(目前)——这是它能铺进从嵌入式到浏览器引擎各种项目的底层原因。

从 C++14 到 C++17:三次版本切换

三个版本的发布节点与要点,来自官方 release notes:

  1. v1.16.0(2025-02-07):要求 C++14,官方声明 1.16.x 是最后支持 C++14 的分支;Bazel 构建改用 Bazel Central Registry 的规范仓库名;测试套件属性在 XML 报告里从属性改为元素输出。
  2. v1.17.0(2025-04-30):起强制要求 C++17,与 Google 的基础 C++ 支持政策(Foundational C++ Support Policy)对齐;新增 --gtest_fail_if_no_test_linked 标志(没链接任何测试就让程序失败)与 DistanceFrom() 匹配器(支持用户自定义 abs())。
  3. v1.18.0(2026-08-10):延续 C++17 要求;GTEST_INTERNAL_HAS_STRING_VIEW 直接置 1——C++17 自带 std::string_view,原来的探测代码失去存在意义;其余以 bug 修复为主。

再往前追溯,标准门槛的抬升是一条规律路线:v1.12.x 是最后支持 C++11 的版本,v1.13 到 v1.16 要求 C++14,v1.17 起 C++17。大约每两三年抬一档,每次都跟随 Google 内部的基础 C++ 支持政策。

发版节奏值得单独看一眼:v1.16 到 v1.17 只隔了不到三个月,v1.17 到 v1.18 却隔了十五个月。这不是维护停滞。每个 release notes 开头写着同一条政策:对应的 x.y.x 分支不接受新功能补丁,官方建议直接用最新 commit 构建,只酌情接受个别关键 bug 修复。主分支在 v1.18.0 发布后的近一个月里合入 26 个提交:删掉 Borland、Sun Studio、IBM XL C++ 这批老编译器的 workaround,让 UnorderedElementsAre() 匹配器支持带 sentinel 的容器,修 MSVC 在 _CRT_DECLARE_NONSTDC_NAMES=0 下的编译错误。仓库最近一次推送是 2026 年 9 月 3 日。

Abseil 依赖是下一个门槛。README 的 Coming Soon 只有一条:“We are planning to take a dependency on Abseil.” 对构建系统而言,这意味着未来的集成成本里要算上 Abseil;对还在观望 C++17 的团队,它给了一个明确信号——越晚升级,迁移成本越高。

文档也已全部迁到 GitHub Pages(google.github.io/googletest),仓库内的 docs 目录只剩一个指路的 README。入门从 GoogleTest Primer 开始。

一次测试如何穿过框架

用官方 quickstart 的最小示例,串一遍从代码到报告的路径:

#include <gtest/gtest.h>

TEST(HelloTest, BasicAssertions) {
  EXPECT_STRNE("hello", "world");
  EXPECT_EQ(7 * 6, 42);
}

编译时链接 GTest::gtest_main(自带 main()),CMake 里用 gtest_discover_tests() 把二进制内的测试注册成 CTest 用例。运行时框架自动发现 TEST 宏注册的用例并逐条执行,断言失败会把期望值与实际值一起打印出来。整个过程不需要一行注册代码——这正是 xUnit 加宏展开带来的零样板。

有共享逻辑时,升级成 fixture:

class StackTest : public testing::Test {
 protected:
  void SetUp() override { stack_.push(1); }
  std::vector<int> stack_;
};

TEST_F(StackTest, TopIsLastPushed) {
  EXPECT_EQ(stack_.back(), 1);
}

TEST_F 的每个用例都会新建一个 StackTest 实例,跑完即毁,互不污染。

模拟依赖:GMock 的最小闭环

GMock 是 GoogleTest 区别于多数轻量框架的最大筹码。以官方入门文档里的画图程序为例,先给接口写一个 mock 类——MOCK_METHOD 宏按"返回值、方法名、参数、限定符"的顺序展开:

#include <gmock/gmock.h>

class MockTurtle : public Turtle {
 public:
  MOCK_METHOD(void, PenDown, (), (override));
  MOCK_METHOD(int, GetX, (), (const, override));
};

然后在测试里设定期望并交给被测代码:

#include <gmock/gmock.h>
#include <gtest/gtest.h>

using ::testing::AtLeast;

TEST(PainterTest, CanDrawSomething) {
  MockTurtle turtle;
  EXPECT_CALL(turtle, PenDown())
      .Times(AtLeast(1));

  Painter painter(&turtle);
  EXPECT_TRUE(painter.DrawCircle(0, 0, 10));
}

EXPECT_CALL 声明"这次交互应该发生至少一次",配合 Return()Ge() 这类匹配器与动作,可以精确描述调用次数、参数、返回值和顺序。手写等价 mock 类通常要几十行虚函数转发,GMock 把它压成一行宏加几行期望声明。链接时用 GTest::gmock(或带 main()GTest::gmock_main)。

快速上手:CMake 与 Bazel 两条路

集成方式二选一,都要求 C++17。

CMake(FetchContent)

cmake_minimum_required(VERSION 3.14)
project(my_project)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(FetchContent)
FetchContent_Declare(googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG v1.18.0)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

enable_testing()
add_executable(hello_test hello_test.cc)
target_link_libraries(hello_test GTest::gtest_main)
include(GoogleTest)
gtest_discover_tests(hello_test)

gtest_force_shared_crt 是官方 README 特别标注的一行:GoogleTest 默认静态链接 C 运行时,而 Visual Studio 新工程默认动态链接,不设这个选项会在 Windows 上报 LNK2038: RuntimeLibrary mismatch。官方 quickstart 用 URL 加 commit hash 的写法固定版本,效果与此处的 GIT_TAG 等价,FetchContent 要求 CMake 3.14 及以上。

cmake -S . -B build && cmake --build build && cd build && ctest

Bazel:Google 内部的主构建系统。MODULE.bazel 里声明 bazel_dep(name = "googletest", version = "1.18.0")(BCR 上的实际版本以 registry.bazel.build 为准),BUILD 文件里定义测试目标:

cc_test(
    name = "hello_test",
    size = "small",
    srcs = ["hello_test.cc"],
    deps = [
        "@googletest//:gtest",
        "@googletest//:gtest_main",
    ],
)
bazel test --cxxopt=-std=c++17 --test_output=all //:hello_test

MSVC 下把 --cxxopt 的值换成 /std:c++17

为什么它还是默认答案

事实与推测分开说。可核实的事实有三条:v1.18.0 于 2026-08-10 发布,C++17 门槛从 v1.17 起就是硬约束;Abseil 依赖已列入官方计划;使用方名单里有 Chromium、LLVM、Protocol Buffers、OpenCV 和 Google 内部的大量项目——对需要长期维护的 C++ 代码库,测试框架的生命周期承诺比功能清单更重要,这份名单本身就是承诺的凭证。

至于 AI 编程工具带火了测试框架这类说法,属于推测,短窗口的关注度波动也说明不了太多——一次版本发布就足以制造峰值。能站住的判断是:这套工具在 2026 年仍是 C++ 测试的事实标准之一,与 Catch2 各占生态位。C++17 迁移会让部分项目转投 Catch2 还是引起回流,要更长期的数据才能验证。

常见问题与排查

  • undefined reference to main:测试二进制没链接 _main 变体。CMake 用 GTest::gtest_main,Bazel 用 @googletest//:gtest_main;需要自己写 main() 时(比如全局初始化),链接 gtest 并手写 RUN_ALL_TESTS() 调用。
  • 旧工具链编译失败:v1.17 起强制 C++17,先确认 GCC/Clang/MSVC 版本,再谈框架升级。支持矩阵见 Google 的基础 C++ 支持政策页面。
  • Windows 链接报 LNK2038: RuntimeLibrary mismatch:C 运行时链接方式不一致,设置 gtest_force_shared_crt(见上文 CMake 示例)。
  • ASSERT_* 编译报错(函数返回值非 void)ASSERT_* 失败时从当前函数返回,只能用在返回 void 的函数里,构造与析构函数中也不可用;这类场景改用 EXPECT_*
  • 死亡测试的线程限制:死亡测试靠 fork 子进程实现,多线程环境下 fork 有已知风险(Linux 上框架改用 clone() 缓解)。名字以 DeathTest 结尾的套件会先于其他测试执行;同一行写两个死亡断言会触发一条难以理解的编译错误。
  • 测试间互相干扰:共享状态放进 fixture 或 suite 级 setup;每个用例独立实例化的设计正是为此。

适用边界与采用建议

  • 新 C++ 项目:直接用 v1.18.0,FetchContent、vcpkg、Conan 任选,一步到位 C++17;
  • 存量 C++11 项目:可暂留 v1.12.x;C++14 项目可暂留 v1.16.x——它是最后支持 C++14 的分支。但升级路径只有一条:先升工具链,再升框架,Abseil 依赖落地后成本只增不减;
  • 偏好单头文件、BDD 风格或不想引 Google 系依赖的团队:Catch2 是合理替代,断言能力差距已经不大。GoogleTest 的差异优势在死亡测试与 GoogleMock 的深度集成,外加背后 Google 内部规模的持续验证;
  • 超大测试套件:GoogleTest 的 TEST 宏为每个用例生成一个独立的类,加上要编译链接框架本体,编译时间与二进制体积需要纳入评估。

一句话:写 C++ 还没定测试框架,googletest 是默认答案里最安全的那一个;已经在用的,把 C++17 当成今年的一次例行升级做掉。


版本与生态数据截至 2026-09-07:Stars、release 日期取自 GitHub API;C++ 标准要求与 Abseil 计划出自官方 release notes 与 README。查最新版本看 GitHub Releases 页面,Bazel 可用版本看 Bazel Central Registry,追随最新 commit 构建是官方推荐姿势。

参与讨论

使用 GitHub 登录。欢迎补充事实、异议与实践。