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 - 测试夹具
- 定义测试夹具类,继承 ::testing::Test 类
- 定义 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); // 余额不变
}
生命周期:
- SetUpTestSuite() - 测试套件级别的设置(静态)
- 执行单个测试:
2.1. Constructor() - 测试类的构造函数
2.2. SetUp() - 每个测试前的设置
2.3. 执行测试函数体的内容
2.4. TearDown() - 每个测试后的清理
2.5. Destructor() - 测试类的析构函数 - 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. 定义参数化测试类
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. 定义测试夹具模板
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. 定义测试夹具模板
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.

被折叠的 条评论
为什么被折叠?



