GoogleTest 使用指南 | TEST 语法

GoogleTest 使用指南 | TEST 语法

宏名称用途使用场景
TEST基本测试独立的简单测试
TEST_F测试夹具需要共享设置/清理的测试
TEST_P参数化测试相同逻辑不同输入的测试
TYPED_TEST类型化测试测试模板类/函数
TYPED_TEST_P类型参数化测试更灵活的类型测试
FRIEND_TEST友元测试访问私有成员

TEST - 基本测试

TEST(测试套件名, 测试名)

TEST(TestSuiteName, TestName) {
    // 测试代码
}

命名规则:

  • 测试套件名:通常是被测试类或模块的名称
  • 测试名:描述具体测试的内容
  • 两者都必须是有效的 C++ 标识符(不能包含下划线)

示例:

// 测试数学函数
TEST(MathTest, Addition) {
    EXPECT_EQ(2 + 2, 4);
    EXPECT_EQ(5 + 7, 12);
}

TEST(MathTest, Multiplication) {
    EXPECT_EQ(3 * 4, 12);
    EXPECT_EQ(5 * 0, 0);
}

// 测试字符串操作
TEST(StringTest, Concatenation) {
    std::string s1 = "Hello";
    std::string s2 = "World";
    EXPECT_EQ(s1 + s2, "HelloWorld");
}

TEST(StringTest, Length) {
    std::string str = "Test";
    EXPECT_EQ(str.length(), 4);
}

TEST_F - 测试夹具

  1. 定义测试夹具类,继承 ::testing::Test 类
  2. 定义 SetUp() 和 TearDown() 方法
// 1. 定义测试夹具类
class TestFixtureName : public ::testing::Test {
protected:
    // 成员变量和方法
    void SetUp() override { }      // 每个测试前调用
    void TearDown() override { }   // 每个测试后调用
};

// 2. 使用 TEST_F 定义测试
TEST_F(TestFixtureName, TestName) {
    // 可以直接访问夹具类的成员
}

示例:

// 测试夹具类
class BankAccountTest : public ::testing::Test {
protected:
    // 构造函数(可选)
    BankAccountTest() {
        // 每个测试前都会创建新实例
    }
    
    // 每个测试前的设置
    void SetUp() override {
        account = std::make_unique<BankAccount>("John Doe", 1000.0);
        transaction_log.clear();
    }
    
    // 每个测试后的清理
    void TearDown() override {
        // 通常不需要,因为对象会自动销毁
        account.reset();
    }
    
    // 测试套件级别的设置(静态)
    static void SetUpTestSuite() {
        std::cout << "Starting BankAccount tests\n";
        database_connection = ConnectToTestDatabase();
    }
    
    // 测试套件级别的清理(静态)
    static void TearDownTestSuite() {
        std::cout << "Finishing BankAccount tests\n";
        database_connection.reset();
    }
    
    // 成员变量
    std::unique_ptr<BankAccount> account;
    std::vector<std::string> transaction_log;
    static std::unique_ptr<Database> database_connection;
};

// 静态成员初始化
std::unique_ptr<Database> BankAccountTest::database_connection;

// 使用 TEST_F
TEST_F(BankAccountTest, InitialBalance) {
    EXPECT_EQ(account->GetBalance(), 1000.0);
    EXPECT_EQ(account->GetOwner(), "John Doe");
}

TEST_F(BankAccountTest, Deposit) {
    account->Deposit(500.0);
    EXPECT_EQ(account->GetBalance(), 1500.0);
}

TEST_F(BankAccountTest, Withdraw) {
    account->Withdraw(200.0);
    EXPECT_EQ(account->GetBalance(), 800.0);
}

TEST_F(BankAccountTest, OverdraftProtection) {
    EXPECT_THROW(account->Withdraw(2000.0), std::runtime_error);
    EXPECT_EQ(account->GetBalance(), 1000.0);  // 余额不变
}

生命周期:

  1. SetUpTestSuite() - 测试套件级别的设置(静态)
  2. 执行单个测试:
    2.1. Constructor() - 测试类的构造函数
    2.2. SetUp() - 每个测试前的设置
    2.3. 执行测试函数体的内容
    2.4. TearDown() - 每个测试后的清理
    2.5. Destructor() - 测试类的析构函数
  3. TearDownTestSuite() - 测试套件级别的清理(静态)

示例:

class LifecycleTest : public ::testing::Test {
protected:
    LifecycleTest() { std::cout << "1. Constructor\n"; }
    ~LifecycleTest() { std::cout << "5. Destructor\n"; }
    
    void SetUp() override { std::cout << "2. SetUp\n"; }
    void TearDown() override { std::cout << "4. TearDown\n"; }
    
    static void SetUpTestSuite() { std::cout << "0. SetUpTestSuite\n"; }
    static void TearDownTestSuite() { std::cout << "6. TearDownTestSuite\n"; }
};

TEST_F(LifecycleTest, Test1) {
    std::cout << "3. Test1 body\n";
}

TEST_F(LifecycleTest, Test2) {
    std::cout << "3. Test2 body\n";
}

输出:

0. SetUpTestSuite
1. Constructor
2. SetUp
3. Test1 body
4. TearDown
5. Destructor
1. Constructor
2. SetUp
3. Test2 body
4. TearDown
5. Destructor
6. TearDownTestSuite

TEST_P - 参数化测试

  1. 定义参数化测试类
  2. 定义测试,使用参数进行测试
  3. 实例化测试
// 1. 定义参数化测试类
class TestClassName : public ::testing::TestWithParam<ParamType> {
    // GetParam() 获取当前参数
};

// 2. 定义测试
TEST_P(TestClassName, TestName) {
    ParamType param = GetParam();
    // 使用参数进行测试
    // ...	
}

// 3. 实例化测试
INSTANTIATE_TEST_SUITE_P(
    InstantiationName,
    TestClassName,
    ::testing::Values(param1, param2, ...)
);

示例:

// 测试质数判断函数
class PrimeTest : public ::testing::TestWithParam<int> {
public:
    bool IsPrime(int n) {
        if (n <= 1) return false;
        for (int i = 2; i * i <= n; i++) {
            if (n % i == 0) return false;
        }
        return true;
    }
};

TEST_P(PrimeTest, CheckPrimality) {
    int n = GetParam();
    if (n == 2 || n == 3 || n == 5 || n == 7 || n == 11 || n == 13) {
        EXPECT_TRUE(IsPrime(n)) << n << " should be prime";
    } else if (n <= 15) {
        EXPECT_FALSE(IsPrime(n)) << n << " should not be prime";
    }
}

// 使用 Values 提供参数
INSTANTIATE_TEST_SUITE_P(
    SmallNumbers,
    PrimeTest,
    ::testing::Values(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
);

// 使用 Range 生成参数
INSTANTIATE_TEST_SUITE_P(
    RangeTest,
    PrimeTest,
    ::testing::Range(1, 20, 2)  // 1, 3, 5, 7, 9, 11, 13, 15, 17, 19
);

// 使用 ValuesIn 从容器获取参数
std::vector<int> GetTestNumbers() {
    return {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
}

INSTANTIATE_TEST_SUITE_P(
    FromVector,
    PrimeTest,
    ::testing::ValuesIn(GetTestNumbers())
);

多参数示例(使用元组):

calculator.h:

#pragma once

class Calculator {
public:
    double add(double a, double b);
    double subtract(double a, double b);
    double multiply(double a, double b);
    double divide(double a, double b);
    int factorial(int n);
};

calculator.cpp:

#include "calculator.h"

#include <stdexcept>

double Calculator::add(double a, double b)
{
    return a + b;
}

double Calculator::subtract(double a, double b)
{
    return a - b;
}

double Calculator::multiply(double a, double b)
{
    return a * b;
}

double Calculator::divide(double a, double b)
{
    if (b == 0) {
        throw std::invalid_argument("Division by zero");
    }
    return a / b;
}

int Calculator::factorial(int n)
{
    if (n == 0 || n == 1)
        return 1;
    return factorial(n - 1) * n;
}
#include "gtest/gtest.h"
#include "calculator.h"

// 测试计算器的多个操作
class CalculatorTest : public ::testing::TestWithParam<
    std::tuple<double, double, char, double>> {
};

TEST_P(CalculatorTest, Operations) {
    double a = std::get<0>(GetParam());
    double b = std::get<1>(GetParam());
    char op = std::get<2>(GetParam());
    double expected = std::get<3>(GetParam());
    
    Calculator calc;
    double result;
    
    switch (op) {
        case '+': result = calc.add(a, b); break;
        case '-': result = calc.subtract(a, b); break;
        case '*': result = calc.multiply(a, b); break;
        case '/': result = calc.divide(a, b); break;
    }
    
    EXPECT_NEAR(result, expected, 0.0001);
}

INSTANTIATE_TEST_SUITE_P(
    BasicOperations,
    CalculatorTest,
    ::testing::Values(
        std::make_tuple(2.0, 3.0, '+', 5.0),
        std::make_tuple(5.0, 3.0, '-', 2.0),
        std::make_tuple(4.0, 3.0, '*', 12.0),
        std::make_tuple(10.0, 2.0, '/', 5.0)
    )
);

// 使用 Combine 生成笛卡尔积
INSTANTIATE_TEST_SUITE_P(
    CombinedOperations,
    CalculatorTest,
    ::testing::Combine(
        ::testing::Values(1.0, 2.0),      // a
        ::testing::Values(3.0, 4.0),      // b
        ::testing::Values('+', '-'),      // op
        ::testing::Values(4.0, 5.0, -2.0, -3.0)  // expected (需要匹配)
    )
);

注意,CombinedOperations 这个测试会失败一部分。

自定义参数名称示例:

// 自定义参数名称生成器
class CustomNameTest : public ::testing::TestWithParam<int> {};

TEST_P(CustomNameTest, TestWithCustomName) {
    EXPECT_TRUE(GetParam() >= 0);
}

// 自定义名称函数
std::string CustomParamNameGenerator(
    const ::testing::TestParamInfo<int>& info) {
    return "Value_" + std::to_string(info.param);
}

INSTANTIATE_TEST_SUITE_P(
    CustomNames,
    CustomNameTest,
    ::testing::Values(0, 1, 42, 100),
    CustomParamNameGenerator  // 生成名称如:CustomNames/Value_42
);

TYPED_TEST - 类型化测试

  1. 定义测试夹具模板
  2. 声明要测试的类型
  3. 定义测试
// 1. 定义测试夹具模板
template <typename T>
class TypedTestFixture : public ::testing::Test {
    // 使用 TypeParam 访问类型
};

// 2. 声明要测试的类型
typedef ::testing::Types<Type1, Type2, ...> MyTypes;
TYPED_TEST_SUITE(TypedTestFixture, MyTypes);

// 3. 定义测试
TYPED_TEST(TypedTestFixture, TestName) {
    // TypeParam 是当前测试的类型
}

示例 1:STL 容器

// 测试不同类型的容器
template <typename T>
class ContainerTest : public ::testing::Test {
protected:
    T container;
    
    void SetUp() override {
        // 添加测试数据
        container.push_back(1);
        container.push_back(2);
        container.push_back(3);
    }
};

// 要测试的容器类型
typedef ::testing::Types<
    std::vector<int>,
    std::deque<int>,
    std::list<int>
> ContainerTypes;

TYPED_TEST_SUITE(ContainerTest, ContainerTypes);

TYPED_TEST(ContainerTest, InitialSize) {
    EXPECT_EQ(this->container.size(), 3);
}

TYPED_TEST(ContainerTest, Clear) {
    this->container.clear();
    EXPECT_TRUE(this->container.empty());
}

TYPED_TEST(ContainerTest, FrontBack) {
    EXPECT_EQ(this->container.front(), 1);
    EXPECT_EQ(this->container.back(), 3);
}

示例 2:测试数值类型

template <typename T>
class NumericTest : public ::testing::Test {
public:
    T Add(T a, T b) { return a + b; }
    T Multiply(T a, T b) { return a * b; }
};

typedef ::testing::Types<int, long, float, double> NumericTypes;
TYPED_TEST_SUITE(NumericTest, NumericTypes);

TYPED_TEST(NumericTest, Addition) {
    TypeParam a = static_cast<TypeParam>(2);
    TypeParam b = static_cast<TypeParam>(3);
    EXPECT_EQ(this->Add(a, b), static_cast<TypeParam>(5));
}

TYPED_TEST(NumericTest, Multiplication) {
    TypeParam a = static_cast<TypeParam>(4);
    TypeParam b = static_cast<TypeParam>(5);
    EXPECT_EQ(this->Multiply(a, b), static_cast<TypeParam>(20));
}

TYPED_TEST_P - 类型参数化测试

  1. 定义测试夹具模板
  2. 声明测试套件
  3. 定义测试
  4. 注册测试
  5. 实例化测试
// 1. 定义测试夹具模板
template <typename T>
class TypeParamTest : public ::testing::Test {};

// 2. 声明测试套件
TYPED_TEST_SUITE_P(TypeParamTest);

// 3. 定义测试
TYPED_TEST_P(TypeParamTest, TestName1) { }
TYPED_TEST_P(TypeParamTest, TestName2) { }

// 4. 注册测试
REGISTER_TYPED_TEST_SUITE_P(TypeParamTest, TestName1, TestName2);

// 5. 实例化测试
typedef ::testing::Types<Type1, Type2> MyTypes;
INSTANTIATE_TYPED_TEST_SUITE_P(MyInstance, TypeParamTest, MyTypes);

示例:

// 定义通用队列测试
template <typename T>
class QueueTest : public ::testing::Test {
protected:
    std::queue<T> queue;
};

TYPED_TEST_SUITE_P(QueueTest);

TYPED_TEST_P(QueueTest, EmptyByDefault) {
    EXPECT_TRUE(this->queue.empty());
}

TYPED_TEST_P(QueueTest, PushAndSize) {
    this->queue.push(TypeParam());
    EXPECT_EQ(this->queue.size(), 1);
    this->queue.push(TypeParam());
    EXPECT_EQ(this->queue.size(), 2);
}

TYPED_TEST_P(QueueTest, PopReducesSize) {
    this->queue.push(TypeParam());
    this->queue.push(TypeParam());
    this->queue.pop();
    EXPECT_EQ(this->queue.size(), 1);
}

// 注册所有测试
REGISTER_TYPED_TEST_SUITE_P(
    QueueTest,
    EmptyByDefault,
    PushAndSize,
    PopReducesSize
);

// 可以在不同地方用不同类型实例化
typedef ::testing::Types<int, double> NumericTypes;
INSTANTIATE_TYPED_TEST_SUITE_P(Numeric, QueueTest, NumericTypes);

typedef ::testing::Types<std::string, std::vector<int>> ComplexTypes;
INSTANTIATE_TYPED_TEST_SUITE_P(Complex, QueueTest, ComplexTypes);

FRIEND_TEST - 友元测试

// 在被测试类中声明
class MyClass {
private:
    int private_member;
    void private_method();
    
    // 声明测试为友元
    FRIEND_TEST(TestSuiteName, TestName);
};

示例:

bank_vault.h:

#pragma once

#include <string>

class BankVault {
private:
    double secret_balance;
    std::string password;
    
    bool ValidatePassword(const std::string& pwd) {
        return pwd == password;
    }
    
    // 允许特定测试访问私有成员
    FRIEND_TEST(BankVaultTest, AccessPrivateMembers);
    FRIEND_TEST(BankVaultTest, TestPasswordValidation);
    
public:
    BankVault() : secret_balance(1000000), password("secret123") {}
    
    bool Withdraw(double amount, const std::string& pwd) {
        if (ValidatePassword(pwd) && amount <= secret_balance) {
            secret_balance -= amount;
            return true;
        }
        return false;
    }
};

test_bank_vault.cpp:

#include "gtest/gtest.h"
#include "bank_vault.h"

TEST(BankVaultTest, AccessPrivateMembers) {
    BankVault vault;
    // 可以直接访问私有成员
    EXPECT_EQ(vault.secret_balance, 1000000);
    EXPECT_EQ(vault.password, "secret123");
}

TEST(BankVaultTest, TestPasswordValidation) {
    BankVault vault;
    // 可以调用私有方法
    EXPECT_TRUE(vault.ValidatePassword("secret123"));
    EXPECT_FALSE(vault.ValidatePassword("wrong"));
}

TEST(BankVaultTest, TestWithdraw)
{
    BankVault vault;
    EXPECT_TRUE(vault.Withdraw(100, "secret123"));
    EXPECT_FALSE(vault.Withdraw(2000000, "secret123"));
    EXPECT_FALSE(vault.Withdraw(0, "wrong"));
}

结果:

➜  Clang 17.0.0 arm64-apple-darwin24.6.0 "/Users/xiye/CppProjects/unit-test-example/out/build/Clang 17.0.0 arm64-apple-darwin24.6.0/test_bank_vault"
Running main() from /Users/xiye/CppProjects/unit-test-example/out/build/Clang 17.0.0 arm64-apple-darwin24.6.0/_deps/googletest-src/googletest/src/gtest_main.cc
[==========] Running 3 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 3 tests from BankVaultTest
[ RUN      ] BankVaultTest.AccessPrivateMembers
[       OK ] BankVaultTest.AccessPrivateMembers (0 ms)
[ RUN      ] BankVaultTest.TestPasswordValidation
[       OK ] BankVaultTest.TestPasswordValidation (0 ms)
[ RUN      ] BankVaultTest.TestWithdraw
[       OK ] BankVaultTest.TestWithdraw (0 ms)
[----------] 3 tests from BankVaultTest (0 ms total)

[----------] Global test environment tear-down
[==========] 3 tests from 1 test suite ran. (0 ms total)
[  PASSED  ] 3 tests.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值