【单元测试】 Google Test (gtest)使用指南_googletest
Google Test (gtest)
Google Test 是 Google 开源的 C++ 测试框架,用于编写单元测试。它支持:
- 丰富的断言(Assertions)
- 测试套件(Test Suites)和测试用例(Test Cases)
- 测试夹具(Test Fixtures)
- 死亡测试(Death Tests)
- 参数化测试(Value-Parameterized Tests)
- 测试过滤与重复执行
一、安装指南
1. Linux 系统安装
# 安装依赖sudo apt-get install build-essential cmake libgtest-dev# 编译安装cd /usr/src/gtestsudo cmake .sudo makesudo mv lib/libgtest* /usr/lib/# 安装头文件sudo cp -r include/gtest /usr/include
2. Windows 系统安装
# 使用 vcpkgvcpkg install gtest:x64-windows# 配置 Visual Studio1. 工具 > 获取工具和功能 > 安装 \"C++ CMake 工具\"2. 项目属性 > C/C++ > 附加包含目录: 添加 vcpkg\\installed\\x64-windows\\include3. 链接器 > 附加库目录: 添加 vcpkg\\installed\\x64-windows\\lib4. 链接器 > 输入: 添加 gtest.lib; gtest_main.lib
3. macOS 系统安装
# Homebrew 安装brew install googletest# 手动配置 Xcode1. 项目设置 > Build Settings > Header Search Paths: /usr/local/include2. Library Search Paths: /usr/local/lib3. Link Binary With Libraries: 添加 libgtest.a, libgtest_main.a
4. 从源码编译
git clone https://github.com/google/googletest.gitcd googletestmkdir buildcd buildcmake .. # 生成 Makefilemake # 编译sudo make install # 安装到系统(默认 /usr/local)
二、测试框架架构
#mermaid-svg-Q3Ps8zD1Hihoulhb {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Q3Ps8zD1Hihoulhb .error-icon{fill:#552222;}#mermaid-svg-Q3Ps8zD1Hihoulhb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Q3Ps8zD1Hihoulhb .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-Q3Ps8zD1Hihoulhb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Q3Ps8zD1Hihoulhb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Q3Ps8zD1Hihoulhb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Q3Ps8zD1Hihoulhb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Q3Ps8zD1Hihoulhb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Q3Ps8zD1Hihoulhb .marker.cross{stroke:#333333;}#mermaid-svg-Q3Ps8zD1Hihoulhb svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Q3Ps8zD1Hihoulhb .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Q3Ps8zD1Hihoulhb .cluster-label text{fill:#333;}#mermaid-svg-Q3Ps8zD1Hihoulhb .cluster-label span{color:#333;}#mermaid-svg-Q3Ps8zD1Hihoulhb .label text,#mermaid-svg-Q3Ps8zD1Hihoulhb span{fill:#333;color:#333;}#mermaid-svg-Q3Ps8zD1Hihoulhb .node rect,#mermaid-svg-Q3Ps8zD1Hihoulhb .node circle,#mermaid-svg-Q3Ps8zD1Hihoulhb .node ellipse,#mermaid-svg-Q3Ps8zD1Hihoulhb .node polygon,#mermaid-svg-Q3Ps8zD1Hihoulhb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Q3Ps8zD1Hihoulhb .node .label{text-align:center;}#mermaid-svg-Q3Ps8zD1Hihoulhb .node.clickable{cursor:pointer;}#mermaid-svg-Q3Ps8zD1Hihoulhb .arrowheadPath{fill:#333333;}#mermaid-svg-Q3Ps8zD1Hihoulhb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Q3Ps8zD1Hihoulhb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Q3Ps8zD1Hihoulhb .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-Q3Ps8zD1Hihoulhb .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-Q3Ps8zD1Hihoulhb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Q3Ps8zD1Hihoulhb .cluster text{fill:#333;}#mermaid-svg-Q3Ps8zD1Hihoulhb .cluster span{color:#333;}#mermaid-svg-Q3Ps8zD1Hihoulhb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Q3Ps8zD1Hihoulhb :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;} Test Program Test Suite 1 Test Suite 2 Test Case 1 Test Case 2 Test Case 3 Test Case 4
三、高级断言系统
1. 二进制比较
ASSERT_EQ(5, 2+3); // 相等ASSERT_NE(0, 1); // 不等ASSERT_LT(3, 5); // 小于ASSERT_LE(4, 4); // 小于等于ASSERT_GT(10, 5); // 大于ASSERT_GE(7, 7); // 大于等于
2. 浮点数比较
ASSERT_FLOAT_EQ(0.1f, 0.1f); // 精确比较ASSERT_DOUBLE_EQ(0.1, 0.1); // 双精度精确ASSERT_NEAR(3.14159, M_PI, 0.0001); // 允许误差
3. 字符串比较
ASSERT_STREQ(\"hello\", \"hello\"); // C字符串相等ASSERT_STRNE(\"A\", \"B\"); // C字符串不等ASSERT_STRCASEEQ(\"HELLO\", \"hello\"); // 忽略大小写
4. 异常检测
ASSERT_THROW( throw std::runtime_error(\"error\"), std::runtime_error);ASSERT_ANY_THROW(throw 1);ASSERT_NO_THROW(int x = 5);
四、测试夹具深入应用
class DatabaseTest : public ::testing::Test {protected: void SetUp() override { db.connect(\"test_db\"); db.createTable(\"Users\"); } void TearDown() override { db.dropTable(\"Users\"); db.disconnect(); } void insertUser(const std::string& name, int age) { db.execute(\"INSERT INTO Users VALUES (\'\" + name + \"\', \" + std::to_string(age) + \")\"); } Database db;};TEST_F(DatabaseTest, InsertRecord) { insertUser(\"Alice\", 30); ASSERT_EQ(db.rowCount(\"Users\"), 1);}TEST_F(DatabaseTest, DeleteRecord) { insertUser(\"Bob\", 25); db.execute(\"DELETE FROM Users\"); ASSERT_EQ(db.rowCount(\"Users\"), 0);}
五、参数化测试进阶
struct UserData { std::string name; int age; bool isValid;};class UserValidationTest : public ::testing::TestWithParam<UserData> {};TEST_P(UserValidationTest, CheckValidity) { const UserData& data = GetParam(); ASSERT_EQ(validateUser(data.name, data.age), data.isValid);}INSTANTIATE_TEST_SUITE_P( DefaultUsers, UserValidationTest, ::testing::Values( UserData{\"Alice\", 30, true}, UserData{\"\", 20, false}, // 空名字 UserData{\"Bob\", -5, false}, // 负年龄 UserData{\"Charlie\", 150, false} // 年龄过大 ));
六、死亡测试详解
TEST(DeathTest, InvalidPointer) { int* ptr = nullptr; ASSERT_DEATH(*ptr = 5, \"Segmentation fault\"); // 预期段错误}TEST(DeathTest, AssertFailure) { ASSERT_DEBUG_DEATH(assert(false), \"Assertion failed\"); // 调试模式}TEST(DeathTest, ExitCode) { ASSERT_EXIT(exit(1), testing::ExitedWithCode(1), \"\"); // 退出码检查}
七、CMake 高级集成
cmake_minimum_required(VERSION 3.14)project(GtestAdvancedExample)# 自动下载gtestinclude(FetchContent)FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG release-1.12.1)FetchContent_MakeAvailable(googletest)# 主程序add_executable(main_app src/main.cpp)# 测试程序add_executable(tests test/database_test.cpp test/user_test.cpp)target_link_libraries(tests PRIVATE gtest_main PRIVATE gmock_main)# 自动发现测试include(GoogleTest)gtest_discover_tests(tests EXTRA_ARGS --gtest_output=xml:${CMAKE_BINARY_DIR}/test_results.xml)# 添加覆盖率支持if(CMAKE_CXX_COMPILER_ID MATCHES \"GNU|Clang\") target_compile_options(tests PRIVATE --coverage -fprofile-arcs -ftest-coverage) target_link_libraries(tests PRIVATE --coverage)endif()
八、高级执行控制
1. 测试筛选
# 运行特定测试套件./tests --gtest_filter=DatabaseTest.*# 排除特定测试./tests --gtest_filter=-*.DeathTest# 正则表达式匹配./tests --gtest_filter=*Validation*.*Invalid*
2. 测试重复与随机化
# 重复执行100次./tests --gtest_repeat=100# 随机执行顺序./tests --gtest_shuffle --gtest_random_seed=42# 遇到失败时停止./tests --gtest_break_on_failure
3. 输出控制
# XML报告./tests --gtest_output=xml:report.xml# JSON报告./tests --gtest_output=json:report.json# 详细输出./tests --gtest_print_time=0 --gtest_color=no
九、与CI系统集成
GitLab CI 示例
stages: - testgtest: stage: test image: ubuntu:22.04 script: - apt-get update && apt-get install -y build-essential cmake git - git clone https://github.com/google/googletest.git - cd googletest && mkdir build && cd build - cmake .. && make && make install - cd ../../project - mkdir build && cd build - cmake .. && make - ./tests --gtest_output=xml:test_report.xml artifacts: paths: - build/test_report.xml reports: junit: build/test_report.xml
十、性能测试集成
TEST(PerformanceTest, VectorPushBack) { const int N = 1000000; std::vector<int> v; auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < N; ++i) { v.push_back(i); } auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); std::cout << \"Time taken: \" << duration.count() << \" ms\" << std::endl; // 性能断言 ASSERT_LT(duration.count(), 50); // 应在50ms内完成}
十一、常见问题解决方案
-lgtest -lgtest_main -lpthread
链接选项TEST()
或 TEST_F()
--gtest_catch_exceptions=0
禁用异常捕获testing::FLAGS_gtest_death_test_style=\"threadsafe\"
PATH=$(SolutionDir)vcpkg\\installed\\x64-windows\\bin
十二、最佳实践
-
测试命名规范
- 测试套件:被测试类名(如
DatabaseTest
) - 测试用例:行为描述(如
InsertValidRecord_Success
)
- 测试套件:被测试类名(如
-
测试组织
project/├── src/└── test/ ├── unit/ │ ├── database_test.cpp │ └── user_test.cpp ├── integration/ └── performance/
-
测试原则
- 每个测试验证单一行为
- 避免测试间依赖
- 使用夹具减少重复代码
- 优先使用
EXPECT_
而非ASSERT_
除非后续操作无效
-
测试覆盖率
# 生成覆盖率报告gcovr -r . --exclude test/ --html-details coverage.html
Google Mock (gmock) 安装与使用指南
Google Mock (gmock) 是 Google Test (gtest) 的扩展库,用于创建模拟对象(Mock Objects),简化 C++ 单元测试中的依赖隔离。
一、CMake 项目集成
在 CMakeLists.txt
中添加:
find_package(GTest REQUIRED)find_package(GMock REQUIRED)add_executable(MyTests test.cpp)target_link_libraries(MyTests PRIVATE GTest::gtest_main GMock::gmock)
二、核心使用步骤
1. 定义接口类
class DataBase {public: virtual ~DataBase() {} virtual bool Connect(const std::string& host) = 0; virtual int Query(const std::string& sql) = 0;};
2. 创建 Mock 类
#include class MockDataBase : public DataBase {public: MOCK_METHOD(bool, Connect, (const std::string& host), (override)); MOCK_METHOD(int, Query, (const std::string& sql), (override));};
3. 设置 Mock 行为
using ::testing::Return;using ::testing::_;TEST(DatabaseTest, BasicTest) { MockDataBase mockDB; // 设置预期行为 EXPECT_CALL(mockDB, Connect(\"localhost\")) .WillOnce(Return(true)); // 模拟返回 true EXPECT_CALL(mockDB, Query(_)) .WillOnce(Return(100)); // 匹配任意参数,返回 100 // 测试使用 Mock 对象 ASSERT_TRUE(mockDB.Connect(\"localhost\")); ASSERT_EQ(mockDB.Query(\"SELECT * FROM users\"), 100);}
三、常用匹配器(Matchers)
Eq(value)
Ge(value)
Contains(str)
StartsWith(prefix)
_
示例:
EXPECT_CALL(mock, Query(StartsWith(\"SELECT\"))) .WillRepeatedly(Return(200));
四、高级用法
-
序列控制:
using ::testing::InSequence;{ InSequence seq; // 定义顺序 EXPECT_CALL(mock, Step1()); EXPECT_CALL(mock, Step2()); // Step1 必须在 Step2 前调用}
-
多次调用:
EXPECT_CALL(mock, Process()) .Times(3) // 预期调用 3 次 .WillRepeatedly(Return(1));
-
参数保存:
std::string saved_arg;EXPECT_CALL(mock, Log(::testing::_)) .WillOnce(::testing::SaveArg<0>(&saved_arg)); // 保存第一个参数
五、常见问题解决
-
链接错误:
- 确保 CMake 正确找到库:
find_package(GTest REQUIRED)
- 检查链接顺序:先
gtest
再gmock
- 确保 CMake 正确找到库:
-
未调用 Mock 方法:
- 使用
::testing::StrictMock
替代Mock
类,严格检查所有预期调用:::testing::StrictMock<MockDataBase> strictMock;
- 使用
-
多线程问题:
- 使用
InSequence
或After
控制调用顺序 - 避免全局 Mock 对象
- 使用
六、完整示例
#include #include // 接口class Logger {public: virtual void Write(const std::string& msg) = 0;};// Mock 类class MockLogger : public Logger {public: MOCK_METHOD(void, Write, (const std::string& msg), (override));};// 被测类class Service {public: Service(Logger* logger) : logger_(logger) {} void DoWork() { logger_->Write(\"Starting work...\"); // 业务逻辑 logger_->Write(\"Work completed\"); }private: Logger* logger_;};// 测试TEST(ServiceTest, LoggingTest) { MockLogger logger; Service service(&logger); EXPECT_CALL(logger, Write(\"Starting work...\")); EXPECT_CALL(logger, Write(\"Work completed\")); service.DoWork(); // 验证日志调用}
十三、扩展资源
- GoogleTest 官方文档
- GoogleMock 用户指南
- 高级测试技巧
- 测试驱动开发(TDD)实战