【发布时间】:2016-03-31 08:55:00
【问题描述】:
这里是 Java 8,但这是一个(可能)与语言无关的通用单元测试问题。
编写 JUnit 测试的语法很简单,但决定要编写什么测试以及如何测试主要/生产代码是我发现最大的问题挑战。在阅读单元测试最佳实践时,我一遍又一遍地听到同样的话:
测试合约
我相信这样的想法是,单元测试不应该是脆弱的,并且如果方法的实现发生变化也不一定会中断。该方法应定义输入合同 -> 结果/结果,并且测试应旨在验证合同是否得到履行。我想。
假设我有以下方法:
public void doFizzOnBuzz(Buzz buzz, boolean isFoobaz) {
// wsClient is a REST client for a microservice
Widget widget = wsClient.getWidgetByBuzzId(buzz.getId());
if(widget.needsFile()) {
File file = readFileFromFileSystem(buzz.getFile());
if(isFoobaz) {
// Do something with the file (doesn't matter what)
}
}
return;
}
private File readFileFromFileSystem(String filename) {
// Private helper method; implementation doesn't matter here EXCEPT...
// Any checked exceptions that Java might throw (as a result of working)
// with the file system are wrapped in a RuntimeException (hence are now
// unchecked.
// Reads a file from the file system based on the filename/URI you specify
}
所以在这里,我们有一个我们希望为 (doFizzOnBuzz) 编写单元测试的方法。这个方法:
- 有两个参数,
buzz和isFoobaz - 使用类属性
wsClient进行网络/REST 调用 - 调用一个私有辅助方法,该方法不仅适用于外部文件系统,而且“吞下”已检查异常;因此
readFileFromFileSystem可以抛出RuntimeExceptions
我们可以为此编写什么样的单元测试来“测试合同”?
验证输入(buzz 和 isFoobaz)是显而易见的;合同应定义每一个的有效值/状态,以及如果它们无效应发生哪些异常/结果。
但除此之外,我不确定这里的“合同”是什么,这使得为其编写测试非常困难。所以我想这个问题真的应该是“我如何确定单元测试的合同是什么,然后你如何编写针对合同而不是实现的测试?” p>
但是对于 SO 问题来说,这个标题太长了。
【问题讨论】:
-
test the contract 只是意味着给定您输入的先决条件,测试您的程序的后置条件是否成立,您的示例是不完整的,因为您没有给出先决条件和
doFizzOnBuzz的后置条件 -
该方法设计得不好,因此很难以合理的方式进行测试。您在这里承担了多项职责——业务逻辑、“小部件查找”(即依赖关系解析)、文件读取等等。难怪很难测试。
-
合约测试是通过将方法视为黑盒并将各种参数发送给方法来完成的。预计该方法不会遇到任何未记录的错误。因此,在您的情况下,如果您向该方法发送 null Buzz,您将面临可能违反合同的
NullPointerException。如果方法声明了任何抛出异常,你也需要测试这些。 -
我将“定义合同”放在“测试合同”之前。这实际上是 TDD 如何导致更好的设计的一个很好的例子。您需要先为您的方法定义精确的要求,然后再从那里着手。
标签: java unit-testing design-by-contract