Skip to content

GoogleTest — 单元测试框架

GoogleTest(gtest)是 Google 开源的 C++ 测试框架,功能完整,与 CMake 集成良好,是 C++ 项目的测试标准。

安装与配置

cmake
# CMakeLists.txt
include(FetchContent)
FetchContent_Declare(
    googletest
    GIT_REPOSITORY https://github.com/google/googletest.git
    GIT_TAG v1.14.0
)
FetchContent_MakeAvailable(googletest)

enable_testing()

add_executable(my_tests
    test_math.cpp
    test_string.cpp
)
target_link_libraries(my_tests PRIVATE GTest::gtest_main GTest::gmock_main)

include(GoogleTest)
gtest_discover_tests(my_tests)

基本断言

cpp
#include <gtest/gtest.h>

// 测试用例:TEST(测试套件名, 测试名)
TEST(MathTest, Addition) {
    EXPECT_EQ(2 + 2, 4);       // 相等(失败继续)
    ASSERT_EQ(2 + 2, 4);       // 相等(失败停止当前测试)

    EXPECT_NE(1, 2);            // 不相等
    EXPECT_LT(1, 2);            // 小于
    EXPECT_LE(1, 2);            // 小于等于
    EXPECT_GT(2, 1);            // 大于
    EXPECT_GE(2, 1);            // 大于等于

    EXPECT_TRUE(1 + 1 == 2);
    EXPECT_FALSE(1 + 1 == 3);
}

TEST(StringTest, Operations) {
    std::string s = "hello";
    EXPECT_EQ(s.size(), 5u);
    EXPECT_STREQ("hello", s.c_str());  // C 字符串比较
    EXPECT_STRNE("hello", "world");

    // 浮点比较(避免精度问题)
    EXPECT_FLOAT_EQ(3.14f, 3.14f);
    EXPECT_DOUBLE_EQ(3.14, 3.14);
    EXPECT_NEAR(3.14, 3.141, 0.01);  // 误差范围内
}

测试夹具(Test Fixture)

cpp
#include <gtest/gtest.h>

class DatabaseTest : public ::testing::Test {
protected:
    void SetUp() override {
        // 每个测试前执行
        db_.connect("test_db");
        db_.execute("CREATE TABLE users (id INT, name TEXT)");
    }

    void TearDown() override {
        // 每个测试后执行
        db_.execute("DROP TABLE users");
        db_.disconnect();
    }

    Database db_;  // 共享的测试资源
};

TEST_F(DatabaseTest, InsertUser) {
    db_.execute("INSERT INTO users VALUES (1, 'Alice')");
    auto result = db_.query("SELECT * FROM users");
    EXPECT_EQ(result.size(), 1u);
    EXPECT_EQ(result[0]["name"], "Alice");
}

TEST_F(DatabaseTest, DeleteUser) {
    db_.execute("INSERT INTO users VALUES (1, 'Alice')");
    db_.execute("DELETE FROM users WHERE id=1");
    auto result = db_.query("SELECT * FROM users");
    EXPECT_TRUE(result.empty());
}

// 套件级别的 SetUp/TearDown(整个套件只执行一次)
class ExpensiveTest : public ::testing::Test {
protected:
    static void SetUpTestSuite() {
        // 整个测试套件开始前执行一次
        shared_resource_ = new ExpensiveResource();
    }
    static void TearDownTestSuite() {
        delete shared_resource_;
    }
    static ExpensiveResource* shared_resource_;
};

参数化测试

cpp
#include <gtest/gtest.h>

// 参数化测试:用不同参数运行同一测试
class PrimeTest : public ::testing::TestWithParam<int> {};

TEST_P(PrimeTest, IsPrime) {
    int n = GetParam();
    EXPECT_TRUE(is_prime(n));
}

INSTANTIATE_TEST_SUITE_P(
    PrimeNumbers,
    PrimeTest,
    ::testing::Values(2, 3, 5, 7, 11, 13, 17, 19, 23)
);

// 多参数组合
class AddTest : public ::testing::TestWithParam<std::tuple<int, int, int>> {};

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

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

异常测试

cpp
TEST(ExceptionTest, ThrowsOnInvalidInput) {
    // 期望抛出特定异常
    EXPECT_THROW(parse_int("abc"), std::invalid_argument);

    // 期望抛出任何异常
    EXPECT_ANY_THROW(risky_operation());

    // 期望不抛出异常
    EXPECT_NO_THROW(safe_operation());

    // 捕获并检查异常内容
    try {
        parse_int("abc");
        FAIL() << "应该抛出异常";
    } catch (const std::invalid_argument& e) {
        EXPECT_STREQ(e.what(), "invalid integer");
    }
}

Google Mock

cpp
#include <gmock/gmock.h>

// 定义接口
class Database {
public:
    virtual ~Database() = default;
    virtual bool connect(const std::string& url) = 0;
    virtual std::vector<Row> query(const std::string& sql) = 0;
    virtual bool execute(const std::string& sql) = 0;
};

// 创建 Mock
class MockDatabase : public Database {
public:
    MOCK_METHOD(bool, connect, (const std::string& url), (override));
    MOCK_METHOD(std::vector<Row>, query, (const std::string& sql), (override));
    MOCK_METHOD(bool, execute, (const std::string& sql), (override));
};

TEST(UserServiceTest, CreateUser) {
    MockDatabase mock_db;

    // 设置期望
    EXPECT_CALL(mock_db, connect("test_db"))
        .Times(1)
        .WillOnce(::testing::Return(true));

    EXPECT_CALL(mock_db, execute(::testing::HasSubstr("INSERT")))
        .Times(1)
        .WillOnce(::testing::Return(true));

    UserService service(&mock_db);
    service.connect("test_db");
    bool ok = service.create_user("Alice", 30);
    EXPECT_TRUE(ok);
    // 测试结束时自动验证所有期望
}

运行测试

bash
# 运行所有测试
./my_tests

# 过滤测试
./my_tests --gtest_filter="MathTest.*"
./my_tests --gtest_filter="*Addition*"
./my_tests --gtest_filter="-*Slow*"  # 排除

# 重复运行(检测偶发失败)
./my_tests --gtest_repeat=100

# 输出 XML 报告
./my_tests --gtest_output=xml:report.xml

# CMake 运行
ctest --output-on-failure
ctest -R "MathTest" --verbose

关键认知

测试夹具(TEST_F)是组织测试的核心,SetUp/TearDown 保证每个测试的独立性。参数化测试避免重复代码。Google Mock 让依赖注入和接口测试变得简单。始终用 EXPECT_* 而非 ASSERT_*,除非后续测试依赖当前断言。

系统学习 C++ 生态,深入底层架构