08.1
为什么需要测试
C++ 错误常在运行期才暴露,且编译慢、反馈周期长。自动化单测能在改动后秒级验证,避免"改一处崩一片"。
测行为边界
重点覆盖正常路径、边界值(0、空、最大)、错误输入,而非逐行实现。
08.2
GoogleTest 基础
GoogleTest 用 TEST 宏定义用例,EXPECT_* 失败继续、ASSERT_* 失败中止当前用例。配合 CMake 的 FetchContent 引入。
#include <gtest/gtest.h>
TEST(MathTest, AddPositive) {
EXPECT_EQ(add(2, 3), 5);
ASSERT_GT(add(1, 1), 1);
}
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}08.3
测试夹具
用 TEST_F + 继承自 ::testing::Test 的类,在 SetUp/TearDown 中准备/清理共享状态,多个用例复用。
class StackTest : public ::testing::Test {
protected:
void SetUp() override { s.push(1); }
std::stack<int> s;
};
TEST_F(StackTest, PopReturnsTop) {
EXPECT_EQ(s.top(), 1);
s.pop();
EXPECT_TRUE(s.empty());
}08.4
Catch2 风格
Catch2 语法更紧凑,用 TEST_CASE 与自然的 REQUIRE 断言,自带 main,单头文件即可集成。
#define CATCH_CONFIG_MAIN
#include <catch2/catch_test_macros.hpp>
TEST_CASE("add works") {
REQUIRE(add(2, 3) == 5);
}选型
大型项目/需要强大 mock 选 GoogleTest;轻量、快速上手选 Catch2。两者都成熟,团队统一即可。
08.5
Mock 隔离
gMock 为接口创建模拟实现,设定期望与返回值,隔离数据库/网络等慢/不稳定依赖,验证交互。
class DBMock : public DB {
public:
MOCK_METHOD(int, query, (int id), (override));
};
TEST(Service, UsesDb) {
DBMock db;
EXPECT_CALL(db, query(1)).WillOnce(testing::Return(42));
EXPECT_EQ(service(db).get(1), 42);
}08.6
覆盖率
用 gcov(GCC)或 llvm-cov(Clang)配合 --coverage 编译,生成覆盖率报告,定位未测代码。
g++ --coverage -O0 test.cpp -o test
./test
lcov --capture --directory . --output-file cov.info
genhtml cov.info --output-directory out08.7
TDD 实践
测试驱动开发:先写失败测试 → 写最小实现让它通过 → 重构。小步快跑,确保每段逻辑都有测试守护。
| 步骤 | 动作 |
|---|---|
| 红 | 写一个会失败的测试 |
| 绿 | 写最少量实现让测试通过 |
| 重构 | 在测试保护下优化结构 |
08.8
常见陷阱与 FAQ
需要继续验证后续断言用 EXPECT;若后续断言依赖前置条件(如对象创建成功),用 ASSERT 提前中止,避免无意义崩溃。
用预编译头、并行构建、把测试按模块拆分,或用 Catch2 单头文件减少翻译单元。CI 缓存构建产物。
可能测了易测的、漏了分支。关注"关键分支覆盖率"而非整体数字,并补边界与异常路径。
08.9