C++ 手写 List 容器的单元测试:Google Test 框架应用指南

一、测试环境准备
  1. 安装 Google Test

    git clone https://github.com/google/googletest.git
    cd googletest
    mkdir build && cd build
    cmake .. && make
    sudo make install
    

  2. 项目结构示例

    project/
    ├── include/
    │   └── MyList.h    # 自定义List实现
    ├── tests/
    │   ├── CMakeLists.txt
    │   └── ListTest.cpp # 测试代码
    └── src/            # List实现源码
    

二、核心测试场景设计
#include "gtest/gtest.h"
#include "MyList.h"

// 测试基础功能
TEST(ListTest, BasicFunctionality) {
    MyList<int> list;
    
    // 空表检查
    ASSERT_TRUE(list.empty());
    ASSERT_EQ(list.size(), 0);
    
    // 添加元素
    list.push_back(42);
    ASSERT_FALSE(list.empty());
    ASSERT_EQ(list.size(), 1);
    ASSERT_EQ(list.front(), 42);
    ASSERT_EQ(list.back(), 42);
    
    // 删除元素
    list.pop_back();
    ASSERT_TRUE(list.empty());
}

三、边界条件测试
TEST(ListTest, BoundaryConditions) {
    MyList<std::string> list;
    
    // 空表操作异常检测
    EXPECT_THROW(list.pop_front(), std::out_of_range);
    EXPECT_THROW(list.front(), std::out_of_range);
    
    // 单元素操作
    list.push_front("test");
    ASSERT_EQ(list.size(), 1);
    list.pop_back();
    ASSERT_TRUE(list.empty());
    
    // 大规模数据测试
    for(int i = 0; i < 10000; ++i) {
        list.push_back(std::to_string(i));
    }
    ASSERT_EQ(list.size(), 10000);
}

四、迭代器功能验证
TEST(ListTest, IteratorOperations) {
    MyList<int> list = {1, 2, 3, 4, 5};  // 假设支持初始化列表
    
    // 前向迭代
    int sum = 0;
    for(auto it = list.begin(); it != list.end(); ++it) {
        sum += *it;
    }
    ASSERT_EQ(sum, 15);
    
    // 反向迭代
    sum = 0;
    for(auto it = list.rbegin(); it != list.rend(); ++it) {
        sum += *it;
    }
    ASSERT_EQ(sum, 15);
}

五、内存管理测试
TEST(ListTest, MemoryManagement) {
    // 内存泄漏检测(需结合Valgrind)
    {
        MyList<int>* plist = new MyList<int>();
        for(int i = 0; i < 100; ++i) {
            plist->push_back(i);
        }
        delete plist;  // 应释放所有节点
    }
    
    // 拷贝语义验证
    MyList<int> list1;
    list1.push_back(10);
    MyList<int> list2 = list1;  // 拷贝构造
    ASSERT_EQ(list2.front(), 10);
    
    // 移动语义验证
    MyList<int> list3 = std::move(list1);
    ASSERT_TRUE(list1.empty());
    ASSERT_EQ(list3.front(), 10);
}

六、CMake 集成配置
cmake_minimum_required(VERSION 3.10)
project(ListTests)

find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIRS} ../include)

add_executable(ListTests ListTest.cpp)
target_link_libraries(ListTests ${GTEST_LIBRARIES} pthread)

七、测试执行与报告
mkdir build && cd build
cmake .. && make
./ListTests  # 执行测试

# 生成XML报告
./ListTests --gtest_output="xml:report.xml"

八、测试覆盖率分析(可选)
# 使用gcov和lcov
g++ --coverage -O0 -g ListTest.cpp -lgtest
./a.out
lcov --capture --directory . --output-file coverage.info
genhtml coverage.info --output-directory cov_report

最佳实践建议

  1. 使用TYPED_TEST模板测试多种数据类型
  2. 结合DeathTest验证断言失败场景
  3. 对每个public方法编写独立测试用例
  4. 使用-fsanitize=address检测内存错误
  5. 测试覆盖率目标应 ≥ 85%

完整测试应覆盖:

  • 插入/删除操作(头/尾/中间)
  • 拷贝/移动语义
  • 异常安全性
  • 迭代器有效性
  • 容量变化
  • 比较运算符
  • 特殊成员函数

通过系统化测试可确保自定义容器的健壮性和标准兼容性。

更多推荐