证明单元测试的正确性

时间:2011-03-04 07:12:42

标签: unit-testing proof correctness

我正在创建一个用于学习目的的图形框架。我正在使用TDD方法,因此我正在编写大量的单元测试。但是,我还在弄清楚如何证明我的单元测试的正确性

例如,我有这个类(不包括实现,我已经简化了)

public class SimpleGraph(){
 //Returns true on success
 public boolean addEdge(Vertex v1, Vertex v2) { ... }

 //Returns true on sucess
 public boolean addVertex(Vertex v1) { ... }
}

我也创建了这个单元测试

@Test
public void SimpleGraph_addVertex_noSelfLoopsAllowed(){
 SimpleGraph g = new SimpleGraph();
 Vertex v1 = new Vertex('Vertex 1');
 actual = g.addVertex(v1);
 boolean expected = false;
 boolean actual = g.addEdge(v1,v1);
 Assert.assertEquals(expected,actual);
}

好的,真棒它有效。这里只有一个关键,我已经证明这些函数只适用于这种情况。然而,在我的图论课程中,我所做的只是在数学上证明定理(归纳,矛盾等等)。

所以我想知道有没有办法证明我的单元测试在数学上是正确的?那么这是一个很好的做法。因此,我们正在测试单元的正确性,而不是针对某个特定结果对其进行测试。

6 个答案:

答案 0 :(得分:5)

没有。在一般情况下,单元测试不会试图证明其正确性。他们应该测试具体的例子。我们的想法是选择足够的代表性示例,如果出现错误,可能会通过一个或多个测试找到它,但您无法确保以这种方式捕获所有错误。例如,如果你是单元测试一个添加函数,你可能会测试一些正数,一些是负数,一些是大数,一些是小数,但仅使用这种方法,你很幸运能找到这种实现不起作用的情况: / p>

int add(int a, int b) {
    if (a == 1234567 && b == 2461357) { return 42; }
    return a + b;
}

然而,您可以通过组合单元测试和code coverage来发现此错误。然而,即使有100%的代码覆盖率,也可能存在任何测试都无法捕获的逻辑错误。

可以证明代码的正确性。它被称为formal verification,但它不是单元测试的用途。除了最简单的软件之外,所有这些都很昂贵,所以很少在实践中完成。

答案 1 :(得分:2)

Probably not。单元测试通过详尽的测试来解决问题:

  • 通过在实现行为之前编写测试来验证测试是否有效。
  • 然后你看到测试失败了。
  • 然后,您实现了传递该测试的行为,并且仅执行该测试。切勿编写实现测试所不需要的代码。

答案 2 :(得分:1)

实际上,您所证明的是您的算法的一个案例正在运行,例如,您证明您的执行路径的一个子集是有效的。测试永远不会帮助您在严格的数学意义上证明正确性(除了非常简单的情况)。在一般情况下,这是impossible。测试是解决这个问题的一种实用方法,我们试图表明代表性案例是正确的(边界值,中间某些值等等),并希望这种方法有效。

但是,一些工具(如findbugs等)可以为您提供代码某些属性的保守证明。

如果你想要你的东西的正式证明,总是CoqAgda和类似的语言,但这是一个单独测试的地狱:)

对于测试与证据的一个简单而简单的介绍是Abstract Interpretation in a Nutshell Patrick Cousot。

答案 3 :(得分:0)

有正式指定代码操作方式的工具,甚至可以证明它们以这种方式工作的工具,但它们远离单元测试区域。

Java世界的两个例子是JMLESC/Java2

NASA a whole department致力于formal methods

答案 4 :(得分:0)

我的2美分。以这种方式看待它:你认为你写了一个做某事的函数,但你真正做的是写一个你认为它做了什么的函数。如果你不能写出代码所做的数学证明,你也可以将函数看作hypothesis;你不能确定它总是正确的,但至少它是falsiable

这就是为什么我们编写单元测试(注意:只是其他函数,容易出现错误,叹息),试图伪造假设,找到它不具备的反例。

答案 5 :(得分:0)

如果您想要获取代码的正确属性,您可以(如前面的帖子中所述)应用一些形式验证工具。这不是一件容易的事,但可能仍然可行。像KeY system这样的工具能够证明Java代码的一阶属性。 KeY在泛型,浮点数和并行性等方面存在一些问题,但对于Java语言的大多数概念都很有效。此外,您可以automatically create test cases with KeY基于证明树。

如果你熟悉JML(这不是很难学,基本上是带有一些逻辑的Java),你可以尝试这种方法。对于系统中非常重要的部分,验证可能真的需要考虑;对于代码的其他部分,使用单元测试测试一些可能的跟踪可能已经足够,例如为了避免回归问题。