【发布时间】:2021-10-14 03:25:41
【问题描述】:
前言
I know what UB is,所以我不是在问如何避免它,而是是否有办法让单元测试更能抵抗它,即使它是一种概率方法,这只会使 UB 更有可能变得明显而不是默默地成功通过测试。
问题
假设我想为一个函数编写一个测试,但我做错了,像这样:
#include <gtest/gtest.h>
#include <vector>
int main()
{
std::vector<int> v{0};
for (auto i = 0; i != 100; ++i) {
v.push_back(3); // push a 3
v.pop_back(); // ops, popping the value I just pushed
EXPECT_EQ(v[1], 3); // UB
}
}
在我的机器上,它始终通过;也许程序太简单了,没有理由将 3 真正从它所在的内存区域中抹去pop_back。
因此测试显然不可靠。
有什么方法可以防止这种意外成功的测试,即使是在统计基础上(“在EXPECT_EQ 之前调用这样的函数会降低 UB 刺痛你的机会”)?
上面的代码只是一个例子(我不愿意测试STL);我知道std::vector<T>::at 是一个绑定安全的std::vector<T>::operator[],但这是一种首先防止未定义行为的方法,而我正在徘徊如何防御它。
例如,通过在v.pop_back(); 之后添加*(&v[0] + 1) = 10; 来利用UB 本身,将使测试的不正确性显而易见,至少在我的机器上是这样。
所以我在想一个工具/库/任何东西,比如说,将v 不持有的内存设置为每个可执行行之后的随机值。
【问题讨论】:
-
不,未定义的行为可能完全符合您的(毫无根据的)期望:)
-
使用
v.at(1)看看你会得到什么。 -
是的。如果您想范围检查您的访问权限,请使用
at。如果您不想在超出范围时抛出异常,那么您需要自己进行范围检查。 -
您不能真正针对 UB 进行完全单元测试。它们可能会帮助您捕获一些实例,但不能证明其正确性。但这对于一般的测试来说是正确的。目标是减少缺陷未被发现的可能性。
-
未定义行为的本质是——如果你不走运的话——它可能看起来像你(或你的单元测试)期望的那样工作。如果你幸运的话,它会崩溃。但它也可以将您的浏览器历史记录通过电子邮件发送给您的祖母,然后格式化您的硬盘。
标签: c++ unit-testing googletest undefined-behavior