测试夹具参数化测试与死亡测试
当多个测试需要相同的准备逻辑时,应使用测试夹具。当同一行为需要验证多组输入输出时,应使用参数化测试。当代码预期触发进程终止、断言失败或 abort 时,可以使用死亡测试。
测试夹具
测试夹具继承自 ::testing::Test。每个测试都会创建一个新的夹具对象,因此测试之间不应依赖成员变量的残留状态。
#include <gtest/gtest.h>
#include <string>
#include <unordered_map>
class Config {
public:
void Set(std::string key, std::string value) {
items_[std::move(key)] = std::move(value);
}
std::string Get(const std::string& key) const {
auto it = items_.find(key);
return it == items_.end() ? "" : it->second;
}
private:
std::unordered_map<std::string, std::string> items_;
};
class ConfigTest : public ::testing::Test {
protected:
void SetUp() override {
config.Set("host", "127.0.0.1");
config.Set("port", "8080");
}
Config config;
};
TEST_F(ConfigTest, ReadsExistingValue) {
EXPECT_EQ(config.Get("host"), "127.0.0.1");
}
TEST_F(ConfigTest, MissingValueReturnsEmptyString) {
EXPECT_EQ(config.Get("user"), "");
}
生命周期
| 钩子 | 执行时机 | 常见用途 |
|---|---|---|
SetUpTestSuite |
测试套件开始前执行一次 | 创建昂贵共享资源 |
SetUp |
每个测试执行前 | 初始化对象、准备输入 |
| 测试体 | 当前测试函数 | 执行行为和断言 |
TearDown |
每个测试执行后 | 清理临时状态 |
TearDownTestSuite |
测试套件结束后执行一次 | 释放共享资源 |
参数化测试
参数化测试适合把“同一逻辑,多组数据”表达成表格。
#include <gtest/gtest.h>
bool IsEven(int value) {
return value % 2 == 0;
}
class EvenTest : public ::testing::TestWithParam<std::pair<int, bool>> {
};
TEST_P(EvenTest, ChecksParity) {
const auto [input, expected] = GetParam();
EXPECT_EQ(IsEven(input), expected);
}
INSTANTIATE_TEST_SUITE_P(
BasicValues,
EvenTest,
::testing::Values(
std::pair{0, true},
std::pair{1, false},
std::pair{2, true},
std::pair{-3, false}));
参数化测试的优势是新增样例成本低,失败报告会指出具体参数实例。复杂参数建议定义结构体并提供可读的命名函数。
struct ParseCase {
std::string input;
bool expected_ok;
};
std::string CaseName(const ::testing::TestParamInfo<ParseCase>& info) {
return info.param.expected_ok ? "ValidInput" : "InvalidInput";
}
类型参数化测试
类型参数化测试适合验证模板类在不同类型下满足相同契约。
template <typename T>
class StackTest : public ::testing::Test {
};
using TestTypes = ::testing::Types<int, double, std::string>;
TYPED_TEST_SUITE(StackTest, TestTypes);
TYPED_TEST(StackTest, DefaultConstructedStackIsEmpty) {
std::vector<TypeParam> values;
EXPECT_TRUE(values.empty());
}
死亡测试
死亡测试用于验证代码在错误条件下会终止进程。它通常依赖子进程执行,因此不要在死亡测试中依赖复杂的跨进程共享状态。
#include <gtest/gtest.h>
#include <cassert>
void RequirePositive(int value) {
assert(value > 0);
}
TEST(DeathTest, DiesWhenValueIsNotPositive) {
EXPECT_DEATH(RequirePositive(0), "value > 0");
}
死亡测试常用于:
- 防御式编程中的
assert。 - 不可恢复错误路径。
- 需要保证错误输入不会静默继续的底层库。
选择建议
| 问题 | 推荐方式 |
|---|---|
| 多个测试需要相同初始化 | TEST_F |
| 一个算法要验证很多输入输出 | TEST_P |
| 一个模板类型要验证多种类型 | TYPED_TEST |
| 需要验证进程终止 | EXPECT_DEATH |
| 只需要一个简单函数测试 | TEST |