单元测试用例库libgtest-dev使用技巧总结

张开发
2026/4/15 8:35:18 15 分钟阅读

分享文章

单元测试用例库libgtest-dev使用技巧总结
libgtest-dev使用技巧总结libgtest-dev是Ubuntu/Debian系统上GoogleTest框架的开发包但和许多软件包不同它只提供源代码需要我们手动编译生成库文件--。 包说明与环境准备包的构成运行sudo apt install libgtest-dev后其头文件.h位于/usr/include/gtest而源代码则在/usr/src/gtest目录下。手动编译进入源代码目录使用cmake和make进行编译然后将生成的静态库libgtest.a,libgtest_main.a复制到系统库目录。也可以使用cmake --build一步完成。环境依赖确保系统已安装cmake、make、g或clang等基础编译工具这是构建和使用GoogleTest的前提。 CMake集成与构建使用find_package(推荐)GoogleTest自带CMake配置文件通过find_package(GTest REQUIRED)CMake会自动找到头文件和库文件路径然后直接链接GTest::gtest和GTest::gtest_main即可。手动编译并链接也可以将/usr/src/gtest下的源代码作为add_library的一部分进行编译和链接或通过target_link_libraries直接链接系统库目录下的gtest和gtest_main。添加测试使用include(GoogleTest)和gtest_discover_tests(your_test_target)CMake会自动发现并注册所有测试用例方便后续运行。✍️ 编写测试用例基础测试使用TEST宏来定义一个测试用例一个测试套件可以包含多个测试用例。断言宏GoogleTest提供两类断言EXPECT_*系列失败时报告错误但继续执行用于验证某个属性是否满足。ASSERT_*系列失败时立即终止当前测试函数适用于后续步骤依赖前一步成功的场景。以下是常用的断言宏速查表类别断言宏描述基本比较EXPECT_EQ(val1, val2)val1 val2EXPECT_NE(val1, val2)val1 ! val2EXPECT_LT(val1, val2)val1 val2EXPECT_LE(val1, val2)val1 val2EXPECT_GT(val1, val2)val1 val2EXPECT_GE(val1, val2)val1 val2布尔值检查EXPECT_TRUE(condition)condition为真EXPECT_FALSE(condition)condition为假C字符串比较EXPECT_STREQ(str1, str2)两个C风格字符串内容相同EXPECT_STRNE(str1, str2)两个C风格字符串内容不同EXPECT_STRCASEEQ(str1, str2)忽略大小写内容相同浮点数比较EXPECT_FLOAT_EQ(val1, val2)两个float值近似相等EXPECT_DOUBLE_EQ(val1, val2)两个double值近似相等EXPECT_NEAR(val1, val2, abs_error)两个浮点数差值在绝对误差内异常检查EXPECT_THROW(statement, exception_type)statement抛出指定类型的异常EXPECT_NO_THROW(statement)statement不抛出任何异常谓词断言EXPECT_PRED1(pred, val1)一元谓词pred(val1)返回真EXPECT_PRED2(pred, val1, val2)二元谓词pred(val1, val2)返回真测试夹具使用TEST_F宏和继承::testing::Test的类用于在多个测试用例间共享初始化和清理代码SetUp和TearDown方法。main函数最简单的写法是链接gtest_main库它会提供main函数入口。如果需要自定义行为也可以自己实现main函数。命令行参数InitGoogleTest函数可以解析GoogleTest的命令行参数例如--gtest_filter用于过滤运行特定测试用例。 高级测试技巧参数化测试使用TEST_P宏和INSTANTIATE_TEST_SUITE_P只需编写一次测试逻辑即可使用多组不同参数进行测试。类型化测试对于模板代码可以使用类型化测试来验证不同数据类型下代码的行为是否一致。死亡测试使用EXPECT_DEATH宏来测试程序在特定条件下是否按预期崩溃-。匹配器使用EXPECT_THAT配合灵活的匹配器可以写出更具表达力的断言。例如检查容器是否包含某元素时EXPECT_THAT(my_vector, ::testing::Contains(5))远比手写循环要清晰。模拟测试使用Google Mock模块可以模拟复杂的外部依赖让测试更加独立和可控。 调试与最佳实践调试断言失败可以利用流操作符在断言失败时输出额外的上下文信息帮助快速定位问题。避免命名中的下划线在TEST和TEST_F宏中测试套件名和测试用例名应避免使用下划线因为这是GoogleTest的保留字符可能导致未定义行为。性能测试编写测试时应确保测试用例的执行时间短并尽量减少测试之间的依赖保证每个测试可以独立运行。 总结使用libgtest-dev的关键在于理解它提供的是源码而非预编译库因此安装后需要手动编译。推荐使用CMake配合find_package进行集成这是最可靠的方法。在编写测试时根据测试场景选择合适的宏TESTvsTEST_F并根据断言的重要性区分使用EXPECT_*还是ASSERT_*这些都是写出高效、可维护测试用例的核心要点。TEST_F宏与测试夹具详解TEST_F是 GoogleTest 中用于编写需要共享数据或初始化/清理逻辑的测试用例的宏。当多个测试用例需要对同一组对象进行操作、都需要执行相同的准备如创建数据库连接、初始化容器、打开文件和清理如释放资源、删除临时文件时使用测试夹具可以避免重复代码并保证每个测试用例运行在独立的、干净的环境中。核心概念测试夹具Test Fixture测试夹具是一个继承自::testing::Test的 C 类。你可以在其中定义测试中需要共享的成员变量如被测试的对象、模拟对象、容器等。重写SetUp()方法该方法会在每个TEST_F用例执行前被调用。重写TearDown()方法该方法会在每个TEST_F用例执行后被调用。每个TEST_F都会在一个全新的夹具对象上运行因此即使一个测试用例修改了夹具成员变量的状态也不会影响后续的测试用例。TEST_F与TEST的主要区别特性TESTTEST_F适用场景独立测试无需共享状态多个测试需要相同的初始状态或清理逻辑是否需要夹具类不需要需要继承自::testing::Test宏的第一个参数测试套件名可任意取夹具类名必须是已定义的类访问夹具成员不能可以直接访问protected/public成员SetUp/TearDown无自动调用完整示例测试一个简单的栈Stack类假设我们要测试一个整型栈它支持Push、Pop、Top和IsEmpty操作。多个测试用例都需要一个非空栈和一个空栈使用夹具可以避免在每个测试中重复构造。1. 被测试的类// stack.h #pragma once #include vector #include stdexcept class IntStack { public: void Push(int value) { data_.push_back(value); } void Pop() { if (IsEmpty()) throw std::out_of_range(Stack is empty); data_.pop_back(); } int Top() const { if (IsEmpty()) throw std::out_of_range(Stack is empty); return data_.back(); } bool IsEmpty() const { return data_.empty(); } size_t Size() const { return data_.size(); } private: std::vectorint data_; };2. 定义测试夹具类// stack_test.cpp #include gtest/gtest.h #include stack.h // 夹具类继承 ::testing::Test class IntStackTest : public ::testing::Test { protected: // 可以在派生类中访问的成员 IntStack empty_stack; // 空栈 IntStack stack_with_one; // 含一个元素的栈 IntStack stack_with_two; // 含两个元素的栈 // SetUp 会在每个 TEST_F 之前执行 void SetUp() override { // 初始化非空栈 stack_with_one.Push(100); stack_with_two.Push(10); stack_with_two.Push(20); // empty_stack 保持为空无需操作 } // TearDown 会在每个 TEST_F 之后执行 void TearDown() override { // 这里可以清理资源例如关闭文件、释放堆内存等。 // 对于本例vector 会自动析构无需显式清理。 // 但为了演示我们可以输出日志或置空状态。 // 实际使用时可根据需要实现。 } };3. 使用TEST_F编写测试用例注意TEST_F的第一个参数是夹具类名IntStackTest第二个参数是测试用例名可自取。在测试函数体内可以直接访问夹具类中的protected成员如empty_stack、stack_with_one。// 测试空栈的 IsEmpty TEST_F(IntStackTest, EmptyStackInitiallyEmpty) { EXPECT_TRUE(empty_stack.IsEmpty()); EXPECT_EQ(0u, empty_stack.Size()); } // 测试 Push 后栈不再为空 TEST_F(IntStackTest, PushMakesStackNonEmpty) { empty_stack.Push(42); EXPECT_FALSE(empty_stack.IsEmpty()); EXPECT_EQ(1u, empty_stack.Size()); EXPECT_EQ(42, empty_stack.Top()); } // 测试 Pop 减少元素并改变 Top TEST_F(IntStackTest, PopRemovesTopElement) { ASSERT_EQ(2u, stack_with_two.Size()); // 确保初始状态正确 EXPECT_EQ(20, stack_with_two.Top()); stack_with_two.Pop(); EXPECT_EQ(1u, stack_with_two.Size()); EXPECT_EQ(10, stack_with_two.Top()); stack_with_two.Pop(); EXPECT_TRUE(stack_with_two.IsEmpty()); } // 测试从空栈 Pop 抛出异常 TEST_F(IntStackTest, PopOnEmptyThrows) { EXPECT_THROW(empty_stack.Pop(), std::out_of_range); } // 测试 Top 在空栈时抛出异常 TEST_F(IntStackTest, TopOnEmptyThrows) { EXPECT_THROW(empty_stack.Top(), std::out_of_range); }编译与运行假设目录结构. ├── stack.h └── stack_test.cpp使用 CMake 编译推荐# CMakeLists.txt cmake_minimum_required(VERSION 3.10) project(StackTest) find_package(GTest REQUIRED) include_directories(${GTEST_INCLUDE_DIRS}) add_executable(stack_test stack_test.cpp) target_link_libraries(stack_test GTest::gtest GTest::gtest_main) include(GoogleTest) gtest_discover_tests(stack_test)或者命令行手动编译需先编译好 GoogleTest 静态库g -stdc11 -I/usr/include/gtest -L/usr/lib -lgtest -lgtest_main -pthread stack_test.cpp -o stack_test ./stack_test运行结果示例[] Running 5 tests from 1 test suite. [----------] Global test environment set-up. [----------] 5 tests from IntStackTest [ RUN ] IntStackTest.EmptyStackInitiallyEmpty [ OK ] IntStackTest.EmptyStackInitiallyEmpty (0 ms) [ RUN ] IntStackTest.PushMakesStackNonEmpty [ OK ] IntStackTest.PushMakesStackNonEmpty (0 ms) [ RUN ] IntStackTest.PopRemovesTopElement [ OK ] IntStackTest.PopRemovesTopElement (0 ms) [ RUN ] IntStackTest.PopOnEmptyThrows [ OK ] IntStackTest.PopOnEmptyThrows (0 ms) [ RUN ] IntStackTest.TopOnEmptyThrows [ OK ] IntStackTest.TopOnEmptyThrows (0 ms) [----------] 5 tests from IntStackTest (0 ms total) [----------] Global test environment tear-down [] 5 tests from 1 test suite ran. (0 ms total) [ PASSED ] 5 tests.核心要点与技巧每个TEST_F运行在新对象上GoogleTest 会为每个测试用例创建一个独立的夹具对象调用SetUp()→ 执行测试体 → 调用TearDown()→ 销毁对象。因此即使在某个测试中修改了stack_with_two其他测试用例看到的仍是SetUp()中初始化的状态。夹具成员通常声明为protected以便测试函数可以访问同时避免对外暴露不必要的接口。SetUp()与构造函数原则上初始化工作可以放在构造函数中但推荐使用SetUp()因为SetUp()中可以调用ASSERT_*宏如果失败会立即终止当前测试构造函数中无法很好地处理测试失败。SetUp()失败时GoogleTest 会跳过该测试用例并报告失败而构造函数失败会直接导致测试崩溃。TearDown()与析构函数清理工作通常放在TearDown()中但也可以放在析构函数。两者区别若SetUp()失败TearDown()仍会被调用保证资源释放。析构函数在对象生命周期结束时总会调用但无法报告测试失败不能使用断言。避免夹具类中的静态成员如果需要跨测试用例共享不可变数据可以使用静态成员但注意静态成员不会被重置可能引入测试间耦合。通常更推荐每个用例独立初始化。继承层级可以从现有夹具类派生新的夹具类实现更复杂的复用。命名约定GoogleTest 官方建议夹具类名以Test结尾如IntStackTest但非强制。何时使用TEST_F而非TEST需要多个测试共享相同的初始状态例如都需要一个已登录的用户对象。需要为每个测试执行相同的清理工作例如删除临时文件、关闭网络连接。测试中需要访问一些辅助方法或数据成员这些成员在多个测试中重复出现。如果测试完全独立且没有共享数据直接用TEST更简洁。总结TEST_F 继承::testing::Test的类 测试夹具模式。通过SetUp()/TearDown()提供每个测试用例的前置和后置动作。每个TEST_F运行在独立的夹具实例上保证测试隔离性。适合编写一系列对同一对象或环境进行多种操作验证的测试套件。掌握TEST_F能让你的 GoogleTest 测试代码更清晰、更易维护是单元测试中非常重要的工具。

更多文章