边界测试需覆盖与输入类型和业务逻辑强相关的值,如int参数作数组下标时测-1、0、1、size、size+1,浮点数测0.0、-0.0、dbl_epsilon、infinity等,并用assert_throw等宏验证行为,结合参数化测试和ub检测工具保障可靠性。

边界测试要测哪些值
函数的边界值不是随便挑几个极端数字,而是和输入类型、业务逻辑强相关的。比如 int 参数,不能只测 INT_MAX 和 0,还要看函数实际怎么用:是作为数组下标?还是作为循环次数?或是作为字符串长度?前者必须覆盖 -1(越界)、0(空)、1(最小有效)、size(刚好满)、size+1(溢出);后者若用于 std::vector::at(),负数会直接抛 std::out_of_range,而 operator[] 不检查——这直接影响你是否要在测试里捕获异常。
常见漏掉的边界包括:
- 有符号整型的 INT_MIN(尤其做取反、加减后可能溢出)
- 浮点数的 0.0、-0.0、DBL_EPSILON、INFINITY
- 字符串的空串 ""、单字符、含 '\0' 的非 null-terminated 字符串(如果函数按 C 风格处理)
- 容器类参数的 empty()、size() == 1、size() == max_size()
用 Google Test 写断言时别只靠 ASSERT_EQ
ASSERT_EQ 适合检查返回值相等,但边界测试常需验证行为而非数值,比如崩溃、抛异常、资源泄漏。这时候得换更匹配的宏:
- 输入非法值应抛异常?用
ASSERT_THROW(func(x), std::invalid_argument)或EXPECT_NO_THROW(func(valid_x)) - 函数不该修改输入?用
EXPECT_EQ比较调用前后对象状态,或用const引用传参再加static_cast检查是否真没改 - 涉及指针或内存操作?配合 AddressSanitizer 编译,跑测试时自动报
heap-buffer-overflow或use-after-free
别把所有断言塞进一个 TEST 函数里——每个边界场景单独写 TEST_F 或 TEST,失败时能准确定位是哪个值出问题。
手动构造边界值容易漏,用参数化测试补全
手写十几组 TEST 很累,也难保证覆盖全。Google Test 支持 INSTANTIATE_TEST_SUITE_P,配合 Values 或自定义生成器,把边界值列成数据集:
class ClampTest : public ::testing::TestWithParam<:tuple int>> {};
TEST_P(ClampTest, HandlesBoundaries) {
auto [input, low, high, expected] = GetParam();
EXPECT_EQ(clamp(input, low, high), expected);
}
INSTANTIATE_TEST_SUITE_P(
BoundaryCases, ClampTest,
::testing::Values(
std::make_tuple(-1, 0, 10, 0), // 下溢
std::make_tuple(5, 0, 10, 5), // 正常
std::make_tuple(15, 0, 10, 10), // 上溢
std::make_tuple(0, 0, 0, 0) // 边界重合
)
);
</:tuple>
这样增删边界值只需改 Values 列表,不用动逻辑;也能和 CI 流水线自然集成,每次 PR 都跑全量边界组合。
边界测试通过不等于安全,得看编译器和标准库行为
C++ 标准对很多边界情况是“未定义行为”(UB),比如对 signed int 做 INT_MAX + 1,或解引用 end() 迭代器。测试在你的机器上“没崩”,不代表它在别的平台、别的优化等级下也稳。
真正可靠的边界验证得三层配合:
- 编译时加 -fsanitize=undefined,address,让 UB 直接报错
- 运行时用 valgrind --tool=memcheck 查内存误用(尤其手动 new/delete 场景)
- 静态分析工具如 clang++ -Xclang -analyzer-checker=core 扫潜在越界
最容易被忽略的是:测试代码本身用了未初始化变量、或依赖了特定 ABI 对齐方式——这些会让边界测试结果失真,反而给你错误安全感。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











