Skip to content

测试夹具参数化测试与死亡测试

当多个测试需要相同的准备逻辑时,应使用测试夹具。当同一行为需要验证多组输入输出时,应使用参数化测试。当代码预期触发进程终止、断言失败或 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