C++单元测试gtest技术
本文内容主要涵盖了 Google Test 的基础概念、常用断言、测试夹具、参数化测试、异常测试、死亡测试、覆盖率分析等核心知识点,适用于 C++ 工程师岗位的面试准备。掌握这些内容有助于在实际开发中高效地编写高质量的单元测试。
#include <iostream>
#include <gtest/gtest.h>// 定义一个测试夹具类
class MyFixture : public testing::Test
{
protected:// 在所有测试用例执行前设置void SetUp() override {// 初始化代码,比如创建对象、打开文件等}// 在所有测试用例执行后清理void TearDown() override {// 清理代码,比如释放资源、关闭文件等}// 可以在这里声明一些公共的成员变量或方法,供所有测试用例使用
};// 使用 TEST_F 宏和自定义的测试夹具类来定义测试用例
TEST_F(MyFixture, MyFirstTest)
{// 第一个测试用例的代码EXPECT_TRUE(true);
}TEST_F(MyFixture, MySecondTest)
{// 第二个测试用例的代码EXPECT_EQ(1, 1);
}int main(int argc, char argv)
{::testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}
断言宏
(1)ASSERT_TRUE(condition): 检查条件是否为真。如果条件为假,则终止当前函数并报告失败。
(2)ASSERT_FALSE(condition): 检查条件是否为假。如果条件为真,则终止当前函数并报告失败。
(3)EXPECT_TRUE(condition): 检查条件是否为真。如果条件为假,则报告失败但继续执行后续的断言。
(4)EXPECT_FALSE(condition): 检查条件是否为假。如果条件为真,则报告失败但继续执行后续的断言。
(5)ASSERT_EQ(expected, actual): 检查预期值(expected)和实际值(actual)是否相等。如果不相等,则终止当前函数并报告失败。
(6)EXPECT_EQ(expected, actual): 检查预期值(expected)和实际值(actual)是否相等。如果不相等,则报告失败但继续执行后续的断言。
(7)ASSERT_NE(val1, val2): 检查两个值(val1 和 val2)是否不相等。如果相等,则终止当前函数并报告失败。
(8)EXPECT_NE(val1, val2): 检查两个值(val1 和 val2)是否不相等。如果相等,则报告失败但继续执行后续的断言。
以下是关于 Google Test(gtest) 的常见面试题及参考答案,适用于 C++ 工程师岗位的准备:
8.0.1. Google Test 是什么?它的主要作用是什么?
- Google Test 是 Google 开发的一个 C++ 单元测试框架,用于编写和运行 C++ 程序的自动化测试。
- 主要作用包括:
- 验证函数逻辑是否正确
- 提高代码质量与可维护性
- 支持 TDD(测试驱动开发)
- 支持异常处理、死亡测试等高级功能
8.0.2. 如何编写一个基本的 Google Test 测试用例?
#include <gtest/gtest.h>// 测试用例类
class MyTest : public ::testing::Test {
protected:void SetUp() override {// 初始化代码}void TearDown() override {// 清理代码}
};// 测试用例
TEST_F(MyTest, TestCaseName) {EXPECT_EQ(1 + 1, 2);ASSERT_TRUE(true);
}
SetUp()
:每个测试用例执行前调用TearDown()
:每个测试用例执行后调用TEST_F
:带夹具的测试宏,适合多个测试共享初始化资源
8.0.3. Google Test 中有哪些常用的断言宏?它们的区别是什么?
断言宏 | 类型 | 行为 |
---|---|---|
EXPECT_EQ(a, b) | 检查相等 | 条件失败时输出信息并继续执行 |
ASSERT_EQ(a, b) | 检查相等 | 条件失败时立即终止当前函数 |
EXPECT_TRUE(condition) | 检查真值 | 条件失败时不中断执行 |
ASSERT_FALSE(condition) | 检查假值 | 条件失败时中断执行 |
EXPECT_NEAR(a, b, abs_error) | 浮点数近似比较 | 判断两个浮点数在误差范围内是否相等 |
EXPECT_THROW(statement, exception_type) | 异常测试 | 检查是否抛出指定类型的异常 |
EXPECT_EXIT / EXPECT_DEATH | 死亡测试 | 检查程序是否按预期退出或崩溃 |
8.0.4. TEST 和 TEST_F 的区别是什么?
特性 | TEST | TEST_F |
---|---|---|
是否使用夹具 | 否 | 是 |
是否支持共享初始化/清理代码 | 否 | 是 |
适用场景 | 简单独立测试 | 多个测试共享资源 |
示例:
// 使用 TEST
TEST(SimpleTest, Addition) {EXPECT_EQ(1 + 1, 2);
}// 使用 TEST_F
class MathTest : public ::testing::Test {
public:int value;void SetUp() override { value = 10; }
};TEST_F(MathTest, AddTest) {EXPECT_EQ(value + 5, 15);
}
8.0.5. 如何运行所有测试用例?
./your_test_executable
也可以通过命令行参数控制运行方式:
./your_test_executable --gtest_filter=MyTest.* # 运行特定测试套件
./your_test_executable --gtest_list_tests # 只列出所有测试名称
8.0.6. 如何实现测试夹具(Test Fixture)?
继承 testing::Test
并重写 SetUp()
和 TearDown()
方法:
class ListTest : public ::testing::Test {
protected:std::list<int> myList;void SetUp() override {myList.push_back(1);myList.push_back(2);}void TearDown() override {myList.clear();}
};TEST_F(ListTest, SizeIsCorrect) {EXPECT_EQ(myList.size(), 2);
}
8.0.7. 如何测试异常?
使用 EXPECT_THROW
或 ASSERT_THROW
:
TEST(ExceptionTest, ThrowsOnInvalidInput) {EXPECT_THROW(throw std::invalid_argument("error"), std::invalid_argument);
}
8.0.8. 如何进行死亡测试(Death Test)?
用于验证某个操作是否会引发程序崩溃或调用 exit()
。
TEST(DeathTest, DiesIfNull) {EXPECT_DEATH(function(nullptr), "Assertion.*failed");
}
EXPECT_DEATH
:子进程崩溃且匹配正则表达式即通过测试EXPECT_EXIT
:可以检查退出码
8.0.9. 如何组织多个测试文件?
- 每个源文件包含多个
TEST
或TEST_F
- 所有测试自动注册到 Google Test 框架中
- 在主函数中统一运行所有测试:
int main(int argc, char* argv[]) {::testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}
8.0.10. Google Test 如何支持参数化测试?
使用 INSTANTIATE_TEST_SUITE_P
结合 TEST_P
定义多组输入数据的测试:
class ParamTest : public ::testing::TestWithParam<int> {};TEST_P(ParamTest, IsEven) {int value = GetParam();EXPECT_EQ(value % 2, 0);
}INSTANTIATE_TEST_SUITE_P(EvenNumbers, ParamTest, testing::Values(2, 4, 6, 8));
8.0.11. 如何判断两个浮点数是否“相等”?
使用 EXPECT_FLOAT_EQ
或 EXPECT_NEAR
:
EXPECT_FLOAT_EQ(0.1 + 0.2, 0.3); // 浮点精度内相等
EXPECT_NEAR(0.1 + 0.2, 0.3, 1e-6); // 允许一定误差
8.0.12. 如何跳过某些测试?
使用 DISABLED_
前缀:
TEST(SmokeTest, DISABLED_SlowTest) {// 被禁用的测试
}
运行时可通过过滤器启用:
./your_test_executable --gtest_also_run_disabled_tests
8.0.13. Google Test 支持哪些测试类型?
类型 | 描述 |
---|---|
单元测试 | 对单个函数或类的行为进行测试 |
集成测试 | 多个模块协同工作的测试 |
参数化测试 | 对同一逻辑使用不同输入进行测试 |
死亡测试 | 验证程序是否因非法操作而终止 |
性能测试 | 可结合其他工具评估性能 |
异常测试 | 检查是否抛出期望的异常 |
8.0.14. 如何生成测试覆盖率报告?
结合 [gcov](file://e:\rep\diary\C\StlDemo.cpp.gcov) 和 lcov
工具链:
- 编译时加入覆盖率选项:
g++ -std=c++17 -lgtest -lgcov -fprofile-arcs -ftest-coverage test.cpp
- 运行测试:
./a.out
- 生成覆盖率报告:
lcov --capture --directory . --output-file coverage.info genhtml coverage.info --output-directory out
8.0.15. Google Test 的优势有哪些?
优势 | 描述 |
---|---|
易用性 | 语法简洁,易于集成到项目中 |
可扩展性强 | 支持自定义断言、事件监听器 |
社区支持 | Google 维护,广泛使用于工业界 |
跨平台 | 支持 Linux、Windows、macOS 等 |
支持多种测试风格 | 包括 TDD、BDD、参数化测试等 |
8.0.16. 如何模拟依赖项?是否必须使用 gmock?
- gmock 是 Google 提供的 mocking 框架,通常与 gtest 配合使用。
- 如果只是简单打桩(Stub),可以在测试中手动实现接口模拟。
示例(不使用 gmock):
class MockDatabase {
public:virtual bool connect() { return true; }
};TEST(MockTest, ConnectSuccess) {MockDatabase db;EXPECT_TRUE(db.connect());
}
若需更复杂的模拟行为(如调用次数、参数匹配),推荐使用 gmock
。
8.0.17. 如何组织测试结构?
建议采用以下结构:
tests/
├── unit/
│ ├── math_test.cpp
│ └── string_utils_test.cpp
├── integration/
│ └── network_integration_test.cpp
└── parameterized/└── list_test.cpp
- 每个
.cpp
文件对应一组相关测试 - 使用
CMakeLists.txt
构建测试工程 - 使用
gtest_main
库简化入口函数
8.0.18. 如何调试失败的测试?
- 添加
--gtest_break_on_failure
参数,在断言失败时暂停:./your_test_executable --gtest_break_on_failure
- 使用
--gtest_catch_exceptions=0
让异常直接抛出,便于调试器捕获
8.0.19. 如何将测试结果输出为 XML 报告?
./your_test_executable --gtest_output="xml:report.xml"
- 该 XML 格式可用于 CI 系统解析测试结果
- Jenkins、GitLab CI 等均支持此格式
8.0.20. 如何提升单元测试的质量?
- 覆盖关键路径:确保边界条件、错误分支都被覆盖
- 隔离依赖:使用 mock/fake 替代数据库、网络等外部系统
- 保持原子性:每个测试只验证一个行为
- 命名清晰:测试名应描述被测行为
- 避免副作用:测试之间不应相互影响