如何使用依赖注入模拟文件系统访问?



我有一个类,创建一个新的File对象:

public class MyClass {
public MyClass(String path) {
this.file = new File(this.getFilePath(path)); // this should be replaced with an IFile
}
public String getFilePath(String path) {
// do something with path and return computed value
}
}

现在我想模拟对File的依赖。所以我创建了一个接口IFile:

public interface IFile {
public String[] list();
public String getPath();
// ...
}
对于实际代码,我创建了一个FileWrapper类来实现这个接口并调用java.io.File:
public class FileWrapper implements IFile {
private File file;
public FileWrapper(String path) {
this.file = new File(path);
}
public String[] list() {
return this.file.list();
}
public String getPath() {
return this.file.getPath();
}
}

我最初的想法是在MyClass的构造函数中使用IFile(并为测试创建模拟实现),但这是不可能的,因为IFile只能在MyClass类中创建,因为File构造函数的参数取决于类本身的值。

如何在MyClass内部动态设置file属性的类,使其依赖于接口IFile?

有几个解决方案,但目前我能想到的最灵活的一个是引入一个工厂。这里有许多可能的选项,但其中一些是:

  1. 如果你想在单元测试中使用它,你有Mockito或Spock或类似的东西,你可以通过mock factory并从中返回你想要的东西。在这种情况下,你甚至不需要那些自定义接口/类,如IFile/FileWrapper,你可以直接模拟文件类,如果你使用例如bytebuddy。

可以是这样的

class FileFactory {
File createFile(String path) {
return new File(path);
}
}
class MyClass {

MyClass(FileFactory factory, String path) {
this.file = factory.createFile(path);
}
}

,在单元测试中,您只需要创建模拟FileFactory并将其作为参数传递给MyClass的构造函数。

  1. 或者,如果你不想模拟File类,你可以使用iffile接口和FileWrapper实现,所以工厂看起来像这样:
class FileFactory {
IFile createFile(String path) {
return new FileWrapper(path);
}
}

,但其他事情看起来类似-您只需要在测试中创建模拟工厂并将其传递给MyClass的构造函数。

  1. 如果您不使用框架/库,那么您可以自己实现mock,如下所示:
class MockFileFactory extends FileFactory {
@Override
IFile createFile(String path) {
return new MockFile(path);
}
}
class MockFile extends FileWrapper {
// override existing methods and do what you like to do here
}

或者,您可以去掉IFile接口,而使用File类。在测试中,您需要像这样的模拟版本:

class MockFile extends File {
// override existing methods and do what you like to do here
}

最新更新