6.1. GoogleTest

GoogleTest 是 Google 开源的 C++ 测试框架。

6.1.1. 安装

# Ubuntu
sudo apt install libgtest-dev

# macOS
brew install googletest

# 使用 CMake FetchContent
include(FetchContent)
FetchContent_Declare(
  googletest
  URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip
)
FetchContent_MakeAvailable(googletest)

6.1.2. 基本测试

#include <gtest/gtest.h>

// 简单测试
TEST(MathTest, Addition) {
    EXPECT_EQ(1 + 1, 2);
    EXPECT_NE(1 + 1, 3);
}

// 浮点数比较
TEST(MathTest, FloatingPoint) {
    EXPECT_FLOAT_EQ(1.0f / 3.0f, 0.333333f);
    EXPECT_NEAR(1.0 / 3.0, 0.333333, 0.0001);
}

// 字符串比较
TEST(StringTest, Compare) {
    std::string s = "hello";
    EXPECT_STREQ(s.c_str(), "hello");
    EXPECT_STRNE(s.c_str(), "world");
}

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

6.1.3. 断言类型

// EXPECT_* - 失败后继续执行
EXPECT_TRUE(condition);
EXPECT_FALSE(condition);
EXPECT_EQ(val1, val2);
EXPECT_NE(val1, val2);
EXPECT_LT(val1, val2);
EXPECT_LE(val1, val2);
EXPECT_GT(val1, val2);
EXPECT_GE(val1, val2);

// ASSERT_* - 失败后立即返回
ASSERT_TRUE(condition);
ASSERT_EQ(val1, val2);
// ... 同上

// 异常测试
EXPECT_THROW(statement, ExceptionType);
EXPECT_ANY_THROW(statement);
EXPECT_NO_THROW(statement);

// 死亡测试
EXPECT_DEATH(statement, regex);
EXPECT_EXIT(statement, predicate, regex);

6.1.4. 测试夹具 (Test Fixture)

class StackTest : public ::testing::Test {
protected:
    void SetUp() override {
        stack.push(1);
        stack.push(2);
    }
    
    void TearDown() override {
        // 清理
    }
    
    std::stack<int> stack;
};

TEST_F(StackTest, Pop) {
    EXPECT_EQ(stack.top(), 2);
    stack.pop();
    EXPECT_EQ(stack.top(), 1);
}

TEST_F(StackTest, Size) {
    EXPECT_EQ(stack.size(), 2);
}

6.1.5. 参数化测试

// 值参数化
class AddTest : public ::testing::TestWithParam<std::tuple<int, int, int>> {};

TEST_P(AddTest, Addition) {
    auto [a, b, expected] = GetParam();
    EXPECT_EQ(a + b, expected);
}

INSTANTIATE_TEST_SUITE_P(
    MathTests,
    AddTest,
    ::testing::Values(
        std::make_tuple(1, 2, 3),
        std::make_tuple(0, 0, 0),
        std::make_tuple(-1, 1, 0)
    )
);

// 类型参数化
template<typename T>
class TypedTest : public ::testing::Test {};

using MyTypes = ::testing::Types<int, float, double>;
TYPED_TEST_SUITE(TypedTest, MyTypes);

TYPED_TEST(TypedTest, Works) {
    TypeParam value = 1;
    EXPECT_EQ(value, 1);
}

6.1.6. Mock (GoogleMock)

#include <gmock/gmock.h>

class Database {
public:
    virtual ~Database() = default;
    virtual bool connect(const std::string& url) = 0;
    virtual std::string query(const std::string& sql) = 0;
};

class MockDatabase : public Database {
public:
    MOCK_METHOD(bool, connect, (const std::string& url), (override));
    MOCK_METHOD(std::string, query, (const std::string& sql), (override));
};

TEST(ServiceTest, UsesDatabase) {
    MockDatabase db;
    
    // 设置期望
    EXPECT_CALL(db, connect("localhost"))
        .Times(1)
        .WillOnce(::testing::Return(true));
    
    EXPECT_CALL(db, query(::testing::HasSubstr("SELECT")))
        .WillOnce(::testing::Return("result"));
    
    // 测试代码
    Service service(&db);
    service.run();
}

6.1.7. 高级特性

6.1.7.1. 测试过滤

# 运行特定测试
./test --gtest_filter=MathTest.*
./test --gtest_filter=*Addition*
./test --gtest_filter=-*Slow*  # 排除

# 列出所有测试
./test --gtest_list_tests

6.1.7.2. 输出格式

# XML 输出
./test --gtest_output=xml:result.xml

# JSON 输出 (需要 1.10+)
./test --gtest_output=json:result.json

6.1.7.3. 重复运行

# 重复运行检测不稳定测试
./test --gtest_repeat=100
./test --gtest_shuffle

6.1.8. CMake 集成

# CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(MyProject)

enable_testing()

add_executable(my_tests test.cpp)
target_link_libraries(my_tests GTest::gtest_main)

include(GoogleTest)
gtest_discover_tests(my_tests)

小技巧

GoogleTest 最佳实践:

  1. 测试名称要清晰描述测试内容

  2. 每个测试只测试一件事

  3. 使用测试夹具减少重复

  4. 使用 Mock 隔离依赖

  5. CI 中启用 shuffle 检测测试间依赖