证明单元测试的正确性
我正在创建一个用于学习目的的图形框架。我正在使用 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);
}
好吧,太棒了,它可以工作。这里只有一个症结,我已经证明这些函数只适用于这种情况。然而,在我的图论课程中,我所做的只是用数学方法证明定理(归纳、矛盾等)。
所以我想知道是否有一种方法可以从数学上证明我的单元测试的正确性?那么这方面有没有好的做法呢?因此,我们正在测试该单元的正确性,而不是测试它的某个结果。
I'm creating a graph framework for learning purposes. I'm using a TDD approach, so I'm writing a lot of unit tests. However, I'm still figuring out how to prove the correctness of my unit tests
For example, I have this class (not including the implementation, and I have simplified it)
public class SimpleGraph(){
//Returns true on success
public boolean addEdge(Vertex v1, Vertex v2) { ... }
//Returns true on sucess
public boolean addVertex(Vertex v1) { ... }
}
I also have created this unit tests
@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);
}
Okay, awesome it works. There is only one crux here, I have proved that the functions work for this case only. However, in my graph theory courses, all I'm doing is proving theorems mathematically (induction, contradiction etc. etc.).
So I was wondering is there a way I can prove my unit tests mathematically for correctness? So is there a good practice for this. So we're testing the unit for correctness, instead of testing it for one certain outcome.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
不。单元测试不会尝试证明一般情况下的正确性。他们应该测试具体示例。这个想法是选择足够多的代表性示例,如果存在错误,一个或多个测试可能会发现它,但您不能确保以这种方式捕获所有错误。例如,如果您对 add 函数进行单元测试,您可能会测试一些正数、一些负数、一些大数和一些小数,但单独使用这种方法,您会很幸运地发现此实现不起作用的情况
:但是,可以通过结合单元测试和代码覆盖率来发现此错误。然而,即使代码覆盖率达到 100%,也可能存在任何测试未能发现的逻辑错误。
证明代码的正确性是可能的。它被称为形式验证,但这不是单元测试的用途。除了最简单的软件之外,其他软件的执行成本也很高,因此在实践中很少这样做。
No. Unit tests don't attempt to prove correctness in the general case. They should test specific examples. The idea is to pick enough representative examples that if there is an error it will probably be found by one or more of the tests, but you can't be sure to catch all errors this way. For example if you were unit testing an add function you might test some positive numbers, some negative, some large numbers and some small, but using this approach alone you'd be lucky to find the case where this implementation doesn't work:
You would however be able to spot this error by combining unit testing and code coverage. However even with 100% code coverage there can be logical errors which didn't get caught by any tests.
It is possible to prove code for correctness. It is called formal verification, but it's not what unit tests are for. It's also expensive to do for all but the most simple software so it is rarely done in practice.
可能 不是。单元测试通过详尽的测试来解决问题:
Probably not. Unit tests approach the problem by exhaustive testing:
实际上,您要证明的是您的算法的一种情况正在工作,例如您正在证明执行路径的子集是有效的。测试永远不会帮助您证明严格数学意义上的正确性(除了非常简单的情况)。在一般情况下,这是不可能的。测试是解决这个问题的一种务实方法,我们试图证明代表性案例是正确的(边界值、中间某处的值等),并希望它能起作用。
尽管如此,一些工具(例如 findbugs 等)仍设法为您提供代码某些属性的保守证明。
如果您想要正式证明您的东西,总有 Coq、Agda 和类似的语言,但这对于编写单元测试来说是一个巨大的延伸:)
一个关于测试与测试的简单而伟大的介绍校样是摘要解读Patrick Cousot。
Really, what you're proving is that one case of your algorithm is working, eg you're proving that a subset of your execution paths are valid. Testing will never help you prove correctness in the strict mathematical sense (except for very simple cases). In the general case, this is impossible. Testing is a pragmatic approach to this problem where we try to show representative cases are correct (boundary values, values somewhere in the middle, etc.) and hope that that works.
Still, some tools such as findbugs etc. manage to give you conservative proof of some properties of your code.
If you would like formal proof of your stuff, there's always Coq, Agda and similar languages, but that's a hell of a stretch from writing a unit test :)
One great, simple introduction to testing vs proofs is Abstract Interpretation in a Nutshell Patrick Cousot.
有一些工具可以正式指定代码的运行方式,甚至还有一些工具可以证明它们以这种方式工作,但它们距离单元测试领域很远。
Java 世界中的两个示例是 JML 和 ESC/Java2
NASA 有 整个部门致力于正式方法。
There are tool for formally specifying how your code operates and even tools to proof that they work in that way, but they are far away from unit testing area.
Two examples from the Java world are JML and ESC/Java2
NASA has a whole department dedicated to formal methods.
我的2分钱。这样看:您认为您编写了一个执行某些操作的函数,但您真正所做的是编写一个您认为它执行某些操作的函数。如果您无法从数学上证明代码的作用,您也可以将该函数视为假设;你不能确定它总是正确的,但至少它是可证伪的。
这就是为什么我们编写单元测试(注意:只是其他函数,容易出现错误,叹息),试图证伪假设,找到不成立的反例。
My 2 cents. Look at it this way: you think you wrote a function that does something, but what you really did was writing a function that you think it does something. If you cannot write a mathematically proof of what the code does, you can as well treat the function as a hypothesis; you cannot be sure it will be always correct, but at least it is falsiable.
And that's why we write unit testing (note: just other functions, prone to have bugs, sigh), to try to falsify the hypothesis finding counter-examples with which it does not hold.
如果您想确保代码的正确性,您可以像之前的文章中提到的那样,应用一些形式化验证工具。这不是一件容易做到的事情,但仍然是可行的。有像 Key 系统 这样的工具能够证明 Java 代码的一阶属性。 KeY 在泛型、浮点数和并行性等方面存在一些问题,但对于 Java 语言的大多数概念来说效果很好。此外,您可以根据证明树自动使用KeY创建测试用例。
如果您熟悉 JML(这并不难学,基本上是 Java 加上一点逻辑),您可以尝试这种方法。对于系统中真正关键的部分,验证可能确实是需要考虑的事情;对于代码的其他部分,通过单元测试测试一些可能的跟踪可能已经足够了,例如避免回归问题。
If you want to go for correctness properties of your code, you can, as already mentioned in previous posts, apply some formal verification tools. This is not an easy thing to do, but may still be doable. There are tools like the KeY system capable of proving first-order properties for Java code. KeY has some problems with things like generics, floats and parallelism, but works quite well for most concepts of the Java language. Moreover, you can automatically create test cases with KeY based on the proof tree.
If you are familiar with JML (this is not hard to learn, basically Java with a bit of logic), you could try out this approach. For really critical parts of your systems, verification might really be something to think about; for other parts of the code, testing some of the possible traces with unit testing might already be sufficient, for example to avoid regression problems.