C++ Google Test(gtest)单元测试入门教程
1. Google Test 是什么Google Test简称 gtest是 Google 开发的一个 C 单元测试框架。简单来说它帮你自动检查代码中的函数、类是否按照预期工作避免每次改完代码都要手动运行程序去验证。比如你写了一个加法函数int Add(int a, int b)你想确认Add(1, 2)是不是真的返回 3。用 gtest 你只需要写一行EXPECT_EQ(Add(1, 2), 3)框架会自动帮你跑这个检查并且告诉你哪些通过了、哪些失败了。2. gtest 有哪些核心功能断言宏用来检查某个值是不是等于预期、某个条件是不是真等比如EXPECT_EQ、ASSERT_TRUE。测试固件如果多个测试用例都需要先做同样的准备工作比如打开文件、创建对象可以统一写在SetUp()里测试完自动清理。参数化测试同一个测试逻辑想用多组不同的数据去跑不用复制粘贴代码gtest 自动帮你生成多个测试用例。死亡测试专门用来测试程序是不是按预期崩溃了比如除零操作、空指针访问等边界情况。事件监听器可以在测试开始、结束、失败时执行自定义操作比如发通知、写日志。3. 快速上手第一个 gtest 程序下面是一个完整的 gtest 示例你复制到项目里就能跑起来#include gtest/gtest.h // 待测试的函数计算两个数的和 int Add(int a, int b) { return a b; } // 测试用例验证 Add 函数对正数是否正常工作 TEST(AddTest, PositiveNumbers) { EXPECT_EQ(Add(1, 2), 3); // 12 应该等于 3 EXPECT_EQ(Add(10, 20), 30); } // 测试用例验证 Add 函数对负数是否正常工作 TEST(AddTest, NegativeNumbers) { EXPECT_EQ(Add(-1, -2), -3); EXPECT_EQ(Add(-5, 5), 0); } int main(int argc, char **argv) { ::testing::InitGoogleTest(argc, argv); return RUN_ALL_TESTS(); // 自动运行所有 TEST 宏定义的测试 }运行结果会像这样[] Running 2 tests from 1 test suite. [----------] Global test environment set-up. [----------] 2 tests from AddTest [ RUN ] AddTest.PositiveNumbers [ OK ] AddTest.PositiveNumbers (0 ms) [ RUN ] AddTest.NegativeNumbers [ OK ] AddTest.NegativeNumbers (0 ms) [----------] 2 tests from AddTest (0 ms total) [] 2 tests from 1 test suite ran. (0 ms total) [ PASSED ] 2 tests.看到[ PASSED ]就说明所有测试都通过了。如果某个EXPECT_EQ失败了gtest 会告诉你期望值和实际值分别是多少方便你定位问题。4. 常用断言宏检查工具gtest 提供两大类断言宏EXPECT_*失败后不终止当前测试继续执行后面的代码适合检查多个条件。ASSERT_*失败后立即终止当前测试函数适合如果这个条件不满足后面的检查没有意义的场景。4.1 布尔值检查断言宏说明EXPECT_TRUE(cond)检查条件为真ASSERT_TRUE(cond)检查条件为真失败立即终止EXPECT_FALSE(cond)检查条件为假ASSERT_FALSE(cond)检查条件为假失败立即终止4.2 数值比较断言宏说明EXPECT_EQ(a, b)检查 a bASSERT_EQ(a, b)检查 a b失败立即终止EXPECT_NE(a, b)检查 a ! bASSERT_NE(a, b)检查 a ! b失败立即终止EXPECT_LT(a, b)检查 a bASSERT_LT(a, b)检查 a b失败立即终止EXPECT_LE(a, b)检查 a bASSERT_LE(a, b)检查 a b失败立即终止EXPECT_GT(a, b)检查 a bASSERT_GT(a, b)检查 a b失败立即终止EXPECT_GE(a, b)检查 a bASSERT_GE(a, b)检查 a b失败立即终止4.3 字符串比较断言宏说明EXPECT_STREQ(s1, s2)检查 C 字符串相等ASSERT_STREQ(s1, s2)检查 C 字符串相等失败立即终止EXPECT_STRNE(s1, s2)检查 C 字符串不相等ASSERT_STRNE(s1, s2)检查 C 字符串不相等失败立即终止EXPECT_STRCASEEQ(s1, s2)检查 C 字符串相等忽略大小写ASSERT_STRCASEEQ(s1, s2)检查 C 字符串相等忽略大小写失败立即终止EXPECT_STRCASENE(s1, s2)检查 C 字符串不相等忽略大小写ASSERT_STRCASENE(s1, s2)检查 C 字符串不相等忽略大小写失败立即终止4.4 浮点数比较断言宏说明EXPECT_FLOAT_EQ(a, b)检查 float 近似相等误差在 4 个 ULP 内ASSERT_FLOAT_EQ(a, b)检查 float 近似相等失败立即终止EXPECT_DOUBLE_EQ(a, b)检查 double 近似相等误差在 4 个 ULP 内ASSERT_DOUBLE_EQ(a, b)检查 double 近似相等失败立即终止EXPECT_NEAR(a, b, eps)检查浮点数 |a - b| epseps 是你自己指定的允许误差ASSERT_NEAR(a, b, eps)检查浮点数 |a - b| eps失败立即终止4.5 异常检查断言宏说明EXPECT_THROW(stmt, exc_type)检查语句是否抛出指定类型的异常ASSERT_THROW(stmt, exc_type)检查语句是否抛出指定类型异常失败立即终止EXPECT_ANY_THROW(stmt)检查语句是否抛出任意异常ASSERT_ANY_THROW(stmt)检查语句是否抛出任意异常失败立即终止EXPECT_NO_THROW(stmt)检查语句不抛出异常ASSERT_NO_THROW(stmt)检查语句不抛出异常失败立即终止4.6 死亡测试检查程序是否崩溃断言宏说明EXPECT_DEATH(stmt, regex)检查语句是否导致程序崩溃且错误信息匹配正则表达式ASSERT_DEATH(stmt, regex)同上失败立即终止EXPECT_EXIT(stmt, code, regex)检查语句是否以指定退出码退出且错误信息匹配正则ASSERT_EXIT(stmt, code, regex)同上失败立即终止4.7 谓词断言自定义检查函数断言宏说明EXPECT_PRED1(pred, a)检查一元谓词 pred(a) 是否为真失败时打印参数值ASSERT_PRED1(pred, a)同上失败立即终止EXPECT_PRED2(pred, a, b)检查二元谓词 pred(a, b) 是否为真ASSERT_PRED2(pred, a, b)同上失败立即终止EXPECT_PRED3(pred, a, b, c)检查三元谓词 pred(a, b, c) 是否为真ASSERT_PRED3(pred, a, b, c)同上失败立即终止EXPECT_PRED4(pred, a, b, c, d)检查四元谓词 pred(a, b, c, d) 是否为真ASSERT_PRED4(pred, a, b, c, d)同上失败立即终止EXPECT_PRED5(pred, a, b, c, d, e)检查五元谓词 pred(a, b, c, d, e) 是否为真ASSERT_PRED5(pred, a, b, c, d, e)同上失败立即终止4.8 类型断言编译期检查断言宏说明::testing::StaticAssertTypeEqT1, T2()编译期检查两个类型是否相同如果不同编译会报错4.9 SCOPED_TRACE定位失败位置宏说明SCOPED_TRACE(message)在作用域内为后续失败添加追踪信息帮助定位失败发生在哪个位置小提示所有ASSERT_*版本在失败时会立即终止当前测试函数而EXPECT_*版本会继续执行后续代码。在循环中使用ASSERT_*需谨慎因为它会直接退出整个测试函数。5. 测试固件Test Fixture—— 让多个测试共享相同的准备工作假设你要测试一个类每个测试用例都需要先创建一个对象、打开一个文件、或者分配一些内存。如果每个测试都重复写这些代码既啰嗦又容易出错。测试固件就是帮你解决这个问题的把公共的初始化代码写在SetUp()里清理代码写在TearDown()里每个测试用例自动执行这些步骤。class MyTestFixture : public ::testing::Test { protected: int value; // 每个测试用例运行前自动调用 void SetUp() override { value 42; // 初始化公共数据 } // 每个测试用例运行后自动调用 void TearDown() override { // 清理资源比如关闭文件、释放内存 } }; // 使用 TEST_F 代替 TEST第一个参数写固件类名 TEST_F(MyTestFixture, TestOne) { EXPECT_EQ(value, 42); // value 已经被 SetUp 初始化为 42 } TEST_F(MyTestFixture, TestTwo) { value 10; EXPECT_EQ(value, 52); // 每个测试用例都是独立的value 重新从 42 开始 }实际场景举例测试文件读写操作时每个测试用例都需要先创建一个临时文件、写入一些数据、测试完再删除文件。用测试固件把这些步骤统一管理代码更干净。class FileTest : public ::testing::Test { protected: FILE* file; void SetUp() override { file fopen(test.txt, w); ASSERT_NE(file, nullptr) lt;lt; 文件打开失败; fprintf(file, Hello, gtest!); } void TearDown() override { if (file) { fclose(file); remove(test.txt); } } }; TEST_F(FileTest, ReadContent) { rewind(file); char buf[64] {0}; fread(buf, 1, 12, file); EXPECT_STREQ(buf, Hello, gtest!); }6. 参数化测试 —— 用一组数据跑同一个测试有时候你想测试一个函数在不同输入下是否都正确。比如测试平方函数你想检查 1、2、3、-2、0 这些输入的结果。手写的话要复制粘贴多个 TEST很麻烦。参数化测试让你只写一次测试逻辑然后提供一组数据gtest 自动为每组数据生成一个独立的测试用例。class SquareTest : public ::testing::TestWithParamstd::tupleint, int { protected: int Square(int x) { return x * x; } }; // 用 TEST_P 代替 TEST TEST_P(SquareTest, HandlesInput) { auto [input, expected] GetParam(); // GetParam() 获取当前这组参数 EXPECT_EQ(Square(input), expected); } // 注册多组参数 (输入, 期望输出) INSTANTIATE_TEST_SUITE_P( SquareTests, SquareTest, ::testing::Values( std::make_tuple(1, 1), std::make_tuple(2, 4), std::make_tuple(3, 9), std::make_tuple(-2, 4), std::make_tuple(0, 0) ) );运行后你会看到 5 个独立的测试用例每个对应一组参数。如果某组失败了gtest 会告诉你具体是哪组数据出了问题。7. 死亡测试与异常检查 —— 测试程序崩溃和抛出异常有些代码在特定情况下应该崩溃比如除零或抛出异常。普通测试很难验证这种情况因为程序一崩溃测试就停了。gtest 的死亡测试专门解决这个问题。// 待测试函数除零操作会崩溃 int Divide(int a, int b) { if (b 0) { fprintf(stderr, Division by zero!\n); abort(); // 主动崩溃 } return a / b; } // 待测试函数抛出异常 int SafeDivide(int a, int b) { if (b 0) throw std::invalid_argument(除数不能为零); return a / b; } // 死亡测试验证程序按预期崩溃 TEST(DeathTest, DivideByZero) { EXPECT_DEATH(Divide(10, 0), Division by zero!); } // 异常检查验证抛出指定异常 TEST(ExceptionTest, ThrowsOnZero) { EXPECT_THROW(SafeDivide(10, 0), std::invalid_argument); EXPECT_NO_THROW(SafeDivide(10, 2)); }EXPECT_DEATH会运行里面的语句如果程序崩溃了并且错误信息包含 Division by zero!测试就算通过。这样你就能放心地测试那些应该崩溃的边界情况了。8. 编译与运行使用 CMake 集成 gtest 的典型配置cmake_minimum_required(VERSION 3.10) project(MyProject) 查找 gtest find_package(GTest REQUIRED) include_directories(${GTEST_INCLUDE_DIRS}) add_executable(test_runner test_main.cpp) target_link_libraries(test_runner ${GTEST_LIBRARIES} pthread)编译后运行./test_runner即可执行所有测试用例。9. 总结Google Test 是 C 生态中最流行的单元测试框架之一。它的核心优势在于断言宏丰富数值、字符串、浮点数、异常、崩溃都能测。测试固件避免重复写初始化和清理代码。参数化测试一组数据自动生成多个测试用例。死亡测试连程序崩溃都能自动化测试。无论是小型项目还是大型工程gtest 都是一个值得推荐的选择。建议你先从第 3 节的示例开始跑通再逐步尝试断言宏、测试固件等高级功能。