我有一个类,创建一个新的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
?
有几个解决方案,但目前我能想到的最灵活的一个是引入一个工厂。这里有许多可能的选项,但其中一些是:
- 如果你想在单元测试中使用它,你有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的构造函数。
- 或者,如果你不想模拟File类,你可以使用iffile接口和FileWrapper实现,所以工厂看起来像这样:
class FileFactory {
IFile createFile(String path) {
return new FileWrapper(path);
}
}
,但其他事情看起来类似-您只需要在测试中创建模拟工厂并将其传递给MyClass的构造函数。
- 如果您不使用框架/库,那么您可以自己实现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
}