Java Path API是Java File API的更好替代品,但大量使用静态方法使其难以在Mockito中进行模拟。
从我的自定义类中,我注入了一个FileSystem
实例,在单元测试期间将其替换为模拟对象。
然而,我需要模拟很多方法(并创建很多模拟对象)才能实现这一点。这在我的测试类中反复发生很多次。因此,我开始考虑设置一个简单的API来注册Path-s并声明相关行为。
例如,我需要检查流打开时的错误处理。
主要类:
class MyClass {
private FileSystem fileSystem;
public MyClass(FileSystem fileSystem) {
this.fileSystem = fileSystem;
}
public void operation() {
String filename = /* such way to retrieve filename, ie database access */
try (InputStream in = Files.newInputStream(fileSystem.getPath(filename))) {
/* file content handling */
} catch (IOException e) {
/* business error management */
}
}
}
测试类:
该测试类:
class MyClassTest {
@Test
public void operation_encounterIOException() {
//Arrange
MyClass instance = new MyClass(fileSystem);
FileSystem fileSystem = mock(FileSystem.class);
FileSystemProvider fileSystemProvider = mock(FileSystemProvider.class);
Path path = mock(Path.class);
doReturn(path).when(fileSystem).getPath("/dir/file.txt");
doReturn(fileSystemProvider).when(path).provider();
doThrow(new IOException("fileOperation_checkError")).when(fileSystemProvider).newInputStream(path, (OpenOption)anyVararg());
//Act
instance.operation();
//Assert
/* ... */
}
@Test
public void operation_normalBehaviour() {
//Arrange
MyClass instance = new MyClass(fileSystem);
FileSystem fileSystem = mock(FileSystem.class);
FileSystemProvider fileSystemProvider = mock(FileSystemProvider.class);
Path path = mock(Path.class);
doReturn(path).when(fileSystem).getPath("/dir/file.txt");
doReturn(fileSystemProvider).when(path).provider();
ByteArrayInputStream in = new ByteArrayInputStream(/* arranged content */);
doReturn(in).when(fileSystemProvider).newInputStream(path, (OpenOption)anyVararg());
//Act
instance.operation();
//Assert
/* ... */
}
}
我有很多类/测试的需求,而模拟设置可能会更加棘手,因为静态方法可能会在Path API上调用3-6个非静态方法。我已经重构了测试以避免大部分冗余代码,但是我的简单API往往非常有限,因为我的Path API使用量增加了。所以又到了重构的时候。
然而,我考虑的逻辑看起来很丑陋,需要很多基本用法的代码。我想要简化API模拟(无论是Java Path API还是其他),这是基于以下原则:
1. 创建实现接口或扩展类进行模拟的抽象类。 2. 实现我不想模拟的方法。 3. 在调用“部分模拟”时,我希望按照以下顺序执行:显式模拟的方法、已实现的方法、默认答案。
为了实现第三步,我考虑创建一个查找实现方法并回退到默认答案的Answer。然后,在模拟创建时传递此Answer的实例。
是否存在直接从Mockito或其他方式处理问题的方法?
operation
方法只是一个模板,我还需要在文件名检索和错误处理方面进行抽象,以及在文件处理方面进行抽象。最后,“MyClass”变成了一个空壳...目前我只有两个“简单”的类:一个驱动调用第二个(“MyClass”)的“批处理”。第一个继承自全局对象,为我们的批处理基础架构定义了公共操作和流程。我不是SRP极端主义者的粉丝 :( - LoganMzz