[英]How can a unit test “test the contract” on a method that returns void?
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
)是显而易见的。 合同应定义每个值的有效值/状态,以及无效的异常/结果。
但是除此之外,我还不确定这到底是什么“合同”,这使得编写测试非常困难。 因此,我想这个问题确实应该是这样的:“ 如何确定单元测试的合同,然后如何编写针对合同而不是实现的测试? ”
但是这个标题对于SO问题来说太长了。
使用doFizzOnBuzz(Buzz buzz, boolean isFoobaz)
和private File readFileFromFileSystem(String filename)
方法的代码不容易测试,因为第一种方法将尝试读取文件,而这不是您要在测试中要做的事情。
在这里, doFizzOnBuzz
需要一些东西来提供文件供其使用。 这个FileProvider
(我称它为)可能是一个接口,类似于:
public interface FileProvider {
File getFile(String filename);
}
当在生产运行,实现真正从磁盘中读取文件时使用,但在单元测试doFizzOnBuzz
模拟实现的FileProvider
可以用来代替。 这将返回一个模拟File
。
要记住的关键点是,在测试doFizzOnBuzz
,我们不会测试提供文件的文件或其他文件。 我们以为可以正常工作。 这些其他代码具有自己的单元测试。
可以使用诸如Mockito之类的模拟框架来创建FileProvider
和File
模拟实现,并可以使用setter将模拟FileProvider
注入FileProvider
类中:
public void setFileProvider(FileProvider f) {
this.fileProvider = f;
}
另外,我不知道wsClient
是什么,我确实知道它具有getWidgetByBuzzId()
方法。 此类也可以是接口,并且出于测试目的,将对接口进行模拟,并返回模拟Widget
,类似于上面的FileProvider。
使用mockito,不仅可以设置接口的模拟实现,还可以定义在该接口上调用方法时返回的值:
//setup mock FileProvider
FileProvider fp = Mockito.mock(FileProvider.class);
//Setup mock File for FileProvider to return
File mockFile = Mockito.mock(File.class);
Mockito.when(mockFile.getName()).thenReturn("mockfilename");
//other methods...
//Make mock FileProvider return mock File
Mockito.when(fp.getFile("filename")).thenReturn(mockFile);
ClassUnderTest test = new ClassUnderTest();
test.setFileProvider(fp); //inject mock file provider
//Also set up mocks for Buzz,, Widget, and anything else
//run test
test.doFizzOnBuzz(...)
//verify that FileProvider.getFile() was actually called:
Mockito.verify(fp).getFile("filenane");
如果未使用参数'filename'调用getFile(),则上述测试将失败
结论如果不能直接观察方法的结果(例如它无效),则可以使用模拟来验证其与其他类和方法的交互。
问题在于您的合同方法无法说明您可以从外部观察到什么效果。 它基本上是一个BiConsumer,因此从确保是否有异常的角度出发,没有太多的单元测试可能。
您可以做的测试是确保调用(模拟的)REST服务,或者在某些情况下该方法会影响File(Buzz参数的一部分,可能指向一个临时文件)。
如果要对方法的输出进行单元测试,则可能需要重构以将确定应该执行的操作(文件需要更新)与实际执行的操作分开。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.