最小闭环是写TEST宏、链接libgtest_main.a、用g++编译时加-lgtest -lgtest_main -pthread;常见错误是未连libgtest_main.a导致undefined reference to 'main'。

怎么在 C++ 项目里快速跑起第一个 GTest 测试
直接能跑通的最小闭环是:写一个 TEST 宏、链接 libgtest_main.a、用 g++ 编译时带上 -lgtest 和 -lgtest_main。别先折腾 CMakeLists 或测试发现机制,先让 EXPECT_EQ(1, 1) 打印出 [ PASSED ]。
常见错误现象:undefined reference to 'main' —— 这是因为只链接了 libgtest.a,没连 libgtest_main.a(它提供了默认 main 入口);或者你自己写了 int main() 又链接了 _main,冲突。
- 推荐做法:用
libgtest_main.a,不自己写main() - 如果必须自定义初始化逻辑(比如设置日志路径),才改用
libgtest.a+ 自己的main(),并在里面调用testing::InitGoogleTest(&argc, argv) - 编译命令示例:
g++ test.cpp -o test -lgtest -lgtest_main -pthread(-pthread必须加,否则多线程断言会卡死)
GTest 的 TEST 和 TEST_F 有什么实际区别
TEST 是无状态测试,适合验证纯函数或独立逻辑;TEST_F(F = fixture)用于需要共享前置资源的场景,比如打开文件、构造对象、连接 mock 数据库——这些操作在每个测试前自动执行,在测试后自动清理。
容易踩的坑:把本该用 TEST_F 的逻辑硬塞进 TEST,结果每个测试都重复 new/delete,不仅慢,还可能因析构顺序或静态变量残留导致偶发失败。
立即学习“C++免费学习笔记(深入)”;
- 用
TEST_F时,fixture 类必须继承testing::Test,且构造/析构函数不能带参数 - 成员变量在每个测试用例中都是全新实例(不是共用),所以不用手动重置状态
- 如果 fixture 构造函数抛异常,对应测试直接标为
FAILED,不会进TEST_F函数体
为什么 EXPECT_EQ(a, b) 不报错但 ASSERT_EQ(a, b) 却跳过了后续断言
EXPECT_* 是“软断言”:失败只记录错误,测试函数继续执行;ASSERT_* 是“硬断言”:失败直接 return,跳过当前函数剩余代码。这不是风格偏好问题,而是语义差异。
典型误用场景:检查指针非空后,紧接着解引用做比较——这里必须用 ASSERT_NE(ptr, nullptr),否则 EXPECT_NE 失败后仍会执行 *ptr,触发段错误。
- 判断前置条件(如输入合法、资源就绪)用
ASSERT_* - 验证业务逻辑输出(如返回值、状态变更)用
EXPECT_* - 没有
ASSERT_TRUE这种东西,只有ASSERT_TRUE(存在),但常用的是ASSERT_EQ、ASSERT_NE、ASSERT_STREQ
CMake 中链接 GTest 容易漏掉的三个点
用 find_package(GTest REQUIRED) 看似简单,但实际项目里常因路径、组件、线程模型不匹配而静默失败。
最常被忽略的是:GTest 默认编译为静态库,但你的项目用了 set(CMAKE_CXX_STANDARD 17),而系统预装的 GTest 是用 C++11 编译的——链接时可能因 ABI 不一致导致 std::string 成员访问异常,现象是测试崩溃在 SetUpTestCase 里,堆栈却看不到你自己的代码。
- 显式指定组件:
find_package(GTest REQUIRED CONFIG COMPONENTS gtest gtest_main)(避免某些发行版把 main 单独打包) - 强制使用动态链接(规避 ABI 问题):
find_package(GTest REQUIRED CONFIG COMPONENTS gtest gtest_main)后加set(GTEST_LIBRARY GTest::gtest),再target_link_libraries(mytest PRIVATE GTest::gtest_main) - 确保线程支持一致:如果项目用了
-D_GLIBCXX_USE_CXX11_ABI=0,GTest 也得用同样 ABI 编译,否则std::vector尺寸对不上
真正麻烦的从来不是“怎么写第一个测试”,而是“为什么这个测试在 CI 上随机挂”——多数时候,是 fixture 生命周期、静态变量初始化顺序、或 ABI 混用在背后咬人。








