【GiraKoo】Google Test (gtest) 单元测试框架

Google Test(简称 gtest)是 Google 开发的 C++ 单元测试框架,提供了丰富的断言宏、测试组织结构和高级功能,是 C++ 项目中最流行的测试框架之一。

核心特性

  • 丰富的断言:提供多种断言宏,支持各种数据类型比较
  • 测试组织:支持测试用例分组和层次化管理
  • 参数化测试:支持数据驱动的测试
  • 测试夹具:提供测试环境的设置和清理
  • 死亡测试:测试程序崩溃或异常退出的情况
  • 跨平台:支持 Windows、Linux、macOS 等平台

基本使用

1. 简单测试示例

cpp 复制代码
#include <gtest/gtest.h>

// 被测试的函数
int Add(int a, int b) {
    return a + b;
}

int Multiply(int a, int b) {
    return a * b;
}

bool IsPrime(int n) {
    if (n <= 1) return false;
    if (n <= 3) return true;
    if (n % 2 == 0 || n % 3 == 0) return false;
    
    for (int i = 5; i * i <= n; i += 6) {
        if (n % i == 0 || n % (i + 2) == 0)
            return false;
    }
    return true;
}

// 基本测试用例
TEST(MathTest, Addition) {
    EXPECT_EQ(Add(2, 3), 5);
    EXPECT_EQ(Add(-1, 1), 0);
    EXPECT_EQ(Add(0, 0), 0);
}

TEST(MathTest, Multiplication) {
    EXPECT_EQ(Multiply(3, 4), 12);
    EXPECT_EQ(Multiply(-2, 5), -10);
    EXPECT_EQ(Multiply(0, 100), 0);
}

TEST(MathTest, PrimeCheck) {
    EXPECT_TRUE(IsPrime(2));
    EXPECT_TRUE(IsPrime(17));
    EXPECT_FALSE(IsPrime(1));
    EXPECT_FALSE(IsPrime(4));
    EXPECT_FALSE(IsPrime(15));
}

int main(int argc, char** argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

2. 常用断言宏

cpp 复制代码
#include <gtest/gtest.h>
#include <string>
#include <vector>

TEST(AssertionTest, BasicAssertions) {
    // 布尔断言
    EXPECT_TRUE(true);
    EXPECT_FALSE(false);
    
    // 相等性断言
    EXPECT_EQ(42, 42);          // 相等
    EXPECT_NE(1, 2);            // 不相等
    EXPECT_LT(1, 2);            // 小于
    EXPECT_LE(1, 1);            // 小于等于
    EXPECT_GT(2, 1);            // 大于
    EXPECT_GE(2, 2);            // 大于等于
}

TEST(AssertionTest, StringAssertions) {
    std::string str1 = "Hello";
    std::string str2 = "World";
    
    EXPECT_STREQ("Hello", str1.c_str());  // C字符串相等
    EXPECT_STRNE(str1.c_str(), str2.c_str());  // C字符串不相等
    EXPECT_EQ(str1, "Hello");             // std::string相等
}

TEST(AssertionTest, FloatingPointAssertions) {
    double a = 1.0;
    double b = 0.9999999;
    
    // 浮点数比较(考虑精度误差)
    EXPECT_DOUBLE_EQ(1.0, 1.0);
    EXPECT_NEAR(a, b, 0.001);  // 在误差范围内相等
}

TEST(AssertionTest, ExceptionAssertions) {
    // 异常断言
    EXPECT_THROW({
        throw std::runtime_error("error");
    }, std::runtime_error);
    
    EXPECT_NO_THROW({
        int x = 1 + 1;
    });
    
    EXPECT_ANY_THROW({
        throw std::logic_error("logic error");
    });
}

3. ASSERT vs EXPECT

cpp 复制代码
TEST(AssertVsExpectTest, Difference) {
    // EXPECT: 失败时继续执行后续测试
    EXPECT_EQ(1, 2);  // 失败,但继续执行
    EXPECT_EQ(3, 3);  // 仍会执行
    
    // ASSERT: 失败时立即停止当前测试
    ASSERT_EQ(1, 1);  // 成功,继续执行
    // 如果上面的ASSERT失败,下面的代码不会执行
    EXPECT_EQ(4, 4);
}

TEST(AssertVsExpectTest, WhenToUseAssert) {
    int* ptr = new int(42);
    
    // 使用ASSERT确保指针有效,避免后续空指针访问
    ASSERT_NE(ptr, nullptr);
    
    // 只有在ptr有效时才执行后续测试
    EXPECT_EQ(*ptr, 42);
    
    delete ptr;
}

测试夹具 (Test Fixtures)

测试夹具用于为多个测试提供共同的设置和清理代码:

cpp 复制代码
#include <gtest/gtest.h>
#include <vector>
#include <memory>

// 被测试的类
class Calculator {
public:
    void Add(int value) { result_ += value; }
    void Subtract(int value) { result_ -= value; }
    void Multiply(int value) { result_ *= value; }
    void Divide(int value) {
        if (value == 0) throw std::invalid_argument("Division by zero");
        result_ /= value;
    }
    void Clear() { result_ = 0; }
    int GetResult() const { return result_; }
    
private:
    int result_ = 0;
};

// 测试夹具类
class CalculatorTest : public ::testing::Test {
protected:
    // 每个测试前调用
    void SetUp() override {
        calc_ = std::make_unique<Calculator>();
        calc_->Clear();
    }
    
    // 每个测试后调用
    void TearDown() override {
        calc_.reset();
    }
    
    // 测试数据
    std::unique_ptr<Calculator> calc_;
};

// 使用测试夹具的测试
TEST_F(CalculatorTest, Addition) {
    calc_->Add(5);
    calc_->Add(3);
    EXPECT_EQ(calc_->GetResult(), 8);
}

TEST_F(CalculatorTest, Subtraction) {
    calc_->Add(10);
    calc_->Subtract(3);
    EXPECT_EQ(calc_->GetResult(), 7);
}

TEST_F(CalculatorTest, Multiplication) {
    calc_->Add(4);
    calc_->Multiply(3);
    EXPECT_EQ(calc_->GetResult(), 12);
}

TEST_F(CalculatorTest, Division) {
    calc_->Add(15);
    calc_->Divide(3);
    EXPECT_EQ(calc_->GetResult(), 5);
}

TEST_F(CalculatorTest, DivisionByZero) {
    calc_->Add(10);
    EXPECT_THROW(calc_->Divide(0), std::invalid_argument);
}

参数化测试

参数化测试允许使用不同的输入数据运行相同的测试逻辑:

cpp 复制代码
#include <gtest/gtest.h>

// 被测试的函数
bool IsEven(int n) {
    return n % 2 == 0;
}

int Factorial(int n) {
    if (n <= 1) return 1;
    return n * Factorial(n - 1);
}

// 参数化测试类
class EvenNumberTest : public ::testing::TestWithParam<int> {
};

// 参数化测试用例
TEST_P(EvenNumberTest, CheckEvenNumbers) {
    int number = GetParam();
    EXPECT_TRUE(IsEven(number));
}

// 实例化参数化测试
INSTANTIATE_TEST_SUITE_P(
    EvenNumbers,
    EvenNumberTest,
    ::testing::Values(0, 2, 4, 6, 8, 10, -2, -4)
);

// 更复杂的参数化测试
struct FactorialTestData {
    int input;
    int expected;
};

class FactorialTest : public ::testing::TestWithParam<FactorialTestData> {
};

TEST_P(FactorialTest, ComputeFactorial) {
    auto data = GetParam();
    EXPECT_EQ(Factorial(data.input), data.expected);
}

INSTANTIATE_TEST_SUITE_P(
    FactorialTestCases,
    FactorialTest,
    ::testing::Values(
        FactorialTestData{0, 1},
        FactorialTestData{1, 1},
        FactorialTestData{2, 2},
        FactorialTestData{3, 6},
        FactorialTestData{4, 24},
        FactorialTestData{5, 120}
    )
);

// 使用范围生成参数
class RangeTest : public ::testing::TestWithParam<int> {
};

TEST_P(RangeTest, SquareIsPositive) {
    int n = GetParam();
    EXPECT_GE(n * n, 0);
}

INSTANTIATE_TEST_SUITE_P(
    NumberRange,
    RangeTest,
    ::testing::Range(-5, 6)  // -5 到 5
);

死亡测试

死亡测试用于验证程序在特定条件下会崩溃或异常退出:

cpp 复制代码
#include <gtest/gtest.h>
#include <cassert>

void CrashFunction() {
    abort();
}

void AssertFunction(bool condition) {
    assert(condition);
}

void ExitFunction(int code) {
    exit(code);
}

TEST(DeathTest, CrashTest) {
    EXPECT_DEATH(CrashFunction(), "");
}

TEST(DeathTest, AssertTest) {
    EXPECT_DEATH(AssertFunction(false), "Assertion.*failed");
}

TEST(DeathTest, ExitTest) {
    EXPECT_EXIT(ExitFunction(1), ::testing::ExitedWithCode(1), "");
    EXPECT_EXIT(ExitFunction(0), ::testing::ExitedWithCode(0), "");
}

// 更安全的死亡测试(在子进程中运行)
TEST(DeathTest, SafeDeathTest) {
    ::testing::FLAGS_gtest_death_test_style = "threadsafe";
    EXPECT_DEATH({
        int* p = nullptr;
        *p = 42;  // 空指针解引用
    }, "");
}

自定义断言和匹配器

cpp 复制代码
#include <gtest/gtest.h>
#include <gmock/gmock.h>

// 自定义断言函数
::testing::AssertionResult IsEvenNumber(int n) {
    if (n % 2 == 0) {
        return ::testing::AssertionSuccess();
    } else {
        return ::testing::AssertionFailure() << n << " is not even";
    }
}

// 自定义匹配器
MATCHER_P(IsDivisibleBy, divisor, "") {
    return (arg % divisor) == 0;
}

MATCHER(IsPositive, "") {
    return arg > 0;
}

TEST(CustomAssertionTest, EvenNumberTest) {
    EXPECT_TRUE(IsEvenNumber(4));
    EXPECT_FALSE(IsEvenNumber(5));
}

TEST(CustomMatcherTest, DivisibilityTest) {
    EXPECT_THAT(15, IsDivisibleBy(3));
    EXPECT_THAT(15, IsDivisibleBy(5));
    EXPECT_THAT(15, ::testing::Not(IsDivisibleBy(4)));
}

TEST(CustomMatcherTest, PositiveTest) {
    EXPECT_THAT(42, IsPositive());
    EXPECT_THAT(-5, ::testing::Not(IsPositive()));
}

// 容器匹配器
TEST(ContainerMatcherTest, VectorTest) {
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    
    EXPECT_THAT(numbers, ::testing::Contains(3));
    EXPECT_THAT(numbers, ::testing::Not(::testing::Contains(6)));
    EXPECT_THAT(numbers, ::testing::ElementsAre(1, 2, 3, 4, 5));
    EXPECT_THAT(numbers, ::testing::SizeIs(5));
}

测试套件的设置和清理

cpp 复制代码
#include <gtest/gtest.h>
#include <fstream>

// 全局设置和清理
class GlobalEnvironment : public ::testing::Environment {
public:
    void SetUp() override {
        // 全局初始化
        std::cout << "Global setup\n";
        // 创建测试文件
        std::ofstream file("test_data.txt");
        file << "test data";
        file.close();
    }
    
    void TearDown() override {
        // 全局清理
        std::cout << "Global teardown\n";
        // 删除测试文件
        std::remove("test_data.txt");
    }
};

// 测试套件级别的设置和清理
class FileTest : public ::testing::Test {
public:
    // 整个测试套件开始前调用一次
    static void SetUpTestSuite() {
        std::cout << "FileTest suite setup\n";
    }
    
    // 整个测试套件结束后调用一次
    static void TearDownTestSuite() {
        std::cout << "FileTest suite teardown\n";
    }
    
protected:
    // 每个测试前调用
    void SetUp() override {
        std::cout << "Individual test setup\n";
    }
    
    // 每个测试后调用
    void TearDown() override {
        std::cout << "Individual test teardown\n";
    }
};

TEST_F(FileTest, ReadFile) {
    std::ifstream file("test_data.txt");
    ASSERT_TRUE(file.is_open());
    
    std::string content;
    std::getline(file, content);
    EXPECT_EQ(content, "test data");
}

TEST_F(FileTest, FileExists) {
    std::ifstream file("test_data.txt");
    EXPECT_TRUE(file.good());
}

int main(int argc, char** argv) {
    ::testing::InitGoogleTest(&argc, argv);
    
    // 注册全局环境
    ::testing::AddGlobalTestEnvironment(new GlobalEnvironment);
    
    return RUN_ALL_TESTS();
}

测试过滤和运行选项

bash 复制代码
# 运行所有测试
./test_executable

# 运行特定测试套件
./test_executable --gtest_filter=MathTest.*

# 运行特定测试用例
./test_executable --gtest_filter=MathTest.Addition

# 排除特定测试
./test_executable --gtest_filter=-MathTest.Multiplication

# 重复运行测试
./test_executable --gtest_repeat=5

# 随机顺序运行测试
./test_executable --gtest_shuffle

# 输出详细信息
./test_executable --gtest_verbose

# 生成XML报告
./test_executable --gtest_output=xml:test_results.xml

CMake 集成示例

cmake 复制代码
# CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(MyProject)

# 设置C++标准
set(CMAKE_CXX_STANDARD 17)

# 查找或下载Google Test
include(FetchContent)
FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        v1.14.0  # 使用最新稳定版本
)
FetchContent_MakeAvailable(googletest)

# 添加可执行文件
add_executable(
  my_tests
  test_main.cpp
  math_test.cpp
  calculator_test.cpp
)

# 链接Google Test
target_link_libraries(
  my_tests
  gtest_main
  gmock_main
)

# 启用测试
enable_testing()

# 添加测试
include(GoogleTest)
gtest_discover_tests(my_tests)

最佳实践

1. 测试命名规范

cpp 复制代码
// 好的命名:描述性强,清晰表达测试意图
TEST(StringUtilsTest, SplitString_WithCommaDelimiter_ReturnsCorrectParts)
TEST(UserManagerTest, CreateUser_WithValidData_ReturnsSuccess)
TEST(DatabaseTest, Connect_WithInvalidCredentials_ThrowsException)

// 避免的命名:过于简单,不够描述性
TEST(Test1, Case1)
TEST(MyTest, TestFunction)

2. 测试组织

cpp 复制代码
// 按功能模块组织测试
namespace math_utils {
    TEST(AdditionTest, PositiveNumbers) { /* ... */ }
    TEST(AdditionTest, NegativeNumbers) { /* ... */ }
    TEST(SubtractionTest, BasicCases) { /* ... */ }
}

namespace string_utils {
    TEST(SplitTest, EmptyString) { /* ... */ }
    TEST(SplitTest, SingleDelimiter) { /* ... */ }
    TEST(TrimTest, WhitespaceOnly) { /* ... */ }
}

3. 测试数据管理

cpp 复制代码
// 使用常量定义测试数据
namespace test_data {
    const std::vector<int> VALID_NUMBERS = {1, 2, 3, 4, 5};
    const std::string SAMPLE_TEXT = "Hello, World!";
    const double TOLERANCE = 1e-6;
}

TEST(DataTest, UseTestConstants) {
    EXPECT_THAT(test_data::VALID_NUMBERS, ::testing::SizeIs(5));
    EXPECT_NEAR(CalculateAverage(test_data::VALID_NUMBERS), 3.0, test_data::TOLERANCE);
}

总结

Google Test 是一个功能强大且易用的 C++ 测试框架,提供了:

  • 丰富的断言宏:支持各种数据类型和条件的验证
  • 灵活的测试组织:测试用例、测试夹具、测试套件
  • 高级功能:参数化测试、死亡测试、自定义匹配器
  • 良好的集成:支持 CMake、CI/CD 等开发工具链
  • 详细的报告:提供清晰的测试结果和失败信息

通过合理使用 Google Test,可以显著提高代码质量,确保软件的可靠性和稳定性。建议在项目开发过程中采用测试驱动开发(TDD)的方式,先写测试再实现功能,这样可以更好地设计 API 并确保代码的正确性。

相关推荐
有冠希没关系22 分钟前
Ffmpeg滤镜
c++
闻缺陷则喜何志丹2 小时前
【并集查找 虚拟节点】P1783 海滩防御|省选-
数据结构·c++·洛谷·并集查找·虚拟节点
用户6853000754752 小时前
双指针法解决力扣922题:按奇偶排序数组II的完整指南
c++
CodeWithMe2 小时前
【读书笔记】《C++ Software Design》第十章与第十一章 The Singleton Pattern & The Last Guideline
开发语言·c++·设计模式
UP_Continue3 小时前
C++--List的模拟实现
开发语言·c++
双叶8363 小时前
(C++)STL标准库(vector动态数组)(list列表)(set集合)(map键值对)相关对比,基础教程
c语言·开发语言·数据结构·c++·list
EutoCool5 小时前
Qt窗口:QToolBar、QStatusBar、QDockWidget、QDialog
开发语言·数据库·c++·嵌入式硬件·qt·前端框架
apocelipes6 小时前
C23和C++26的#embed嵌入资源指南
c语言·c++·开发工具和环境·c23·c++26
归云鹤7 小时前
C++ 右值引用 (Rvalue References)
开发语言·c++