是否可以编写涵盖所有内容的单元测试?

Is it possible to write a unit test that cover everything?

假设我有一个函数

function (int x) {
  if (x < 10) return true;
  return false;
}

理想情况下,您想编写 2^32 - 1 个测试用例来覆盖 INT_MIN 到 INT_MAX?当然这是不切实际的。

为了让生活更轻松,我们为

编写测试用例

这些测试用例很好,但并未涵盖所有用例。假设有一天有人将函数修改为

function (int x) {
  if (x == 12) return true;
  if (x < 10) return true;
  return false;
}

他将运行 测试并意识到所有测试都通过了。我们如何确保在不走极端的情况下涵盖所有场景。我描述的这个问题有关键词吗?

由于您表述问题的方式,这部分是评论,部分是答案。

评论

Is it possible to write a unit test that cover everything?

不。 即使在您的示例中,您将测试用例限制为 2^32 但是如果将代码移动到 64 位系统然后有人添加一行使用 2^34 或其他东西。

另外,您的问题向我表明您正在考虑使用动态代码的静态测试用例,例如代码是动态的,因为它会随着时间的推移被程序员更改,这并不意味着代码会动态修改。您应该考虑使用动态代码进行动态测试用例。

最后你没有注明是白盒测试、灰盒测试还是黑盒测试。

答案

让工具分析代码并生成测试数据。

参见:A Survey on Automatic Test Data Generation

你还问了搜索关键词。

这是我发现有价值的 Google 搜索:

code analysis automated test generation survey

相关

我自己从未使用过这些测试用例工具,因为我使用 Prolog DCG 来生成我的测试用例,目前我正在做的一个项目在大约两分钟内生成了数百万个测试用例并对其进行了测试几分钟。一些失败的测试用例我永远不会自己想出来,所以这可能被一些人认为是矫枉过正,但它确实有效。

由于许多人不知道 Prolog DCG,这里是 Eric Lippert 使用 C# 和 LINQ 解释的类似方式,Every Binary Tree There Is

不,目前没有不涉及某种非常密集的计算(例如测试很多很多案例)的通用算法,但是您可以按照这样的方式编写单元测试在更改方法的情况下,失败的可能性更高。例如,在给出的答案中,写一个 x = 10 的测试。对于其他两种情况,首先选择 11 和 int.Max 之间的几个随机数并测试它们。然后测试 int.Min 和 9 之间的几个随机数。测试不会 necessarily 在您描述的修改之后失败,但是失败的可能性比如果您刚刚对值进行了硬编码。

此外,正如@GuyCoder 在他出色的回答中指出的那样,即使您确实 尝试做类似的事情,也很难(或不可能)证明存在不可能更改会破坏您的测试的方法。

另外,请记住,没有一种测试自动化(包括单元测试)是万无一失的测试方法;即使在理想条件下,您通常也无法 100% 证明您的程序是正确的。请记住,几乎所有的软件测试方法基本上都是经验方法,而经验方法并不能真正达到 100% 的确定性。 (尽管如此,他们可以获得很大的确定性;事实上,许多科学论文达到 95% 或更高的确定性——有时 更高——所以在这种情况下,差异可能并不完全这么重要)。例如,即使您有 100% 的代码覆盖率,您怎么知道某个地方的测试没有错误?您要为测试编写测试吗? (这会导致 turtles all the way down 类型的情况)。

如果你想真正了解它并相信大卫·休谟,你真的永远不可能 100% 确定基于经验测试的事情;每次您 运行 测试都通过了这一事实并不意味着它会在未来继续通过。不过我跑题了。

如果您有兴趣,formal verification 研究演绎证明软件(或至少软件的某些方面)正确的方法。请注意,这样做的主要问题是,对任何复杂的完整系统的程序进行正式验证往往非常困难或不可能,尤其是当您使用未经正式验证的第三方库时. (这些,加上最初学习技术的困难,是形式验证尚未真正在学术界和某些非常狭窄的行业应用之外流行的一些主要原因)。

最后一点:软件附带错误。您很难找到 任何 复杂的系统,它在发布时是 100% 无缺陷的。正如我上面提到的,目前没有任何已知技术可以保证您的测试发现所有错误(如果您能找到一个,您将成为 非常 富有的人),所以在大多数情况下,您将不得不依靠统计措施来了解您是否进行了充分的测试。

TL;DR 不,你不能,即使你可以,你仍然不能 100% 确定你的软件是正确的(可能有错误例如,在你的测试中)。在可预见的未来,您的单元测试用例也将需要维护。不过,您可以将测试编写得 更具弹性