[英]How do I mock Java Path API with Mockito?
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个非静态方法。 我已经重构测试以避免大多数冗余代码,但是随着我的Path API使用量的增加,我的简单API往往非常有限。 因此,再次该重构。
但是,我正在考虑的逻辑很难看,并且需要大量的基本用法代码。 我希望简化API模拟(无论是否为Java Path API)的方式基于以下原则:
为了实现第三步,我考虑创建一个Answer
,用于查找实现的方法并回退到默认答案。 然后在模拟创建时传递此Answer
的实例。
是否有现有的方法可以直接从Mockito或其他解决问题的方法来实现这一目标?
您的问题是您违反了“ 单一责任原则” 。
您有两个问题:
InputStream
您试图用一种方法完成这两项工作,这迫使您要做大量的额外工作。 而是将工作分为两个不同的类。 例如,如果代码是这样构造的:
class MyClass {
private FileSystem fileSystem;
private final StreamProcessor processor;
public MyClass(FileSystem fileSystem, StreamProcessor processor) {
this.fileSystem = fileSystem;
this.processor = processor;
}
public void operation() {
String filename = /* such way to retrieve filename, ie database access */
try (InputStream in = Files.newInputStream(fileSystem.getPath(filename))) {
processor.process(in);
} catch (IOException e) {
/* business error management */
}
}
}
class StreamProcessor {
public StreamProcessor() {
// maybe set dependencies, depending on the need of your app
}
public void process(InputStream in) throws IOException {
/* file content handling */
}
}
现在,我们将职责分为两个地方。 从InputStream
您要测试的所有业务逻辑工作的类,仅需要一个输入流。 实际上,我什至不会嘲笑它,因为它只是数据。 您可以根据需要以任何方式加载InputStream
,例如使用您在问题中提到的ByteArrayInputStream
。 StreamProcessor
测试中不需要Java Path API的任何代码 。
此外,如果您以通用方式访问文件,则只需进行一项测试即可确保该行为有效。 您还可以使StreamProcessor
成为接口,然后在代码库的不同部分中,对不同类型的文件执行不同的工作,同时将不同的StreamProcessor
传递到文件API中。
您在评论中说:
听起来不错,但我必须忍受大量旧代码。 我开始介绍单元测试,并且不想重构太多的“应用程序”代码。
最好的方法是我上面说的。 但是,如果要进行最少的更改以添加测试,则应执行以下操作:
旧代码:
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 */
}
}
新代码:
public void operation() {
String filename = /* such way to retrieve filename, ie database access */
try (InputStream in = Files.newInputStream(fileSystem.getPath(filename))) {
new StreamProcessor().process(in);
} catch (IOException e) {
/* business error management */
}
}
public class StreamProcessor {
public void process(InputStream in) throws IOException {
/* file content handling */
/* just cut-paste the other code */
}
}
这是做我上面描述的最少的侵入方法。 我描述的原始方法更好 ,但是显然它涉及的重构更多。 这种方法应该几乎不涉及其他任何代码更改,但是将允许您编写测试。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.