如何使用依赖注入来模拟文件系统访问?
How to use dependency injection to mock out file system access?
我有一个 class 创建新的 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
class 来实现此接口并调用 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
class 内部创建,因为 File
构造函数的参数取决于 class 本身的值。
如何在MyClass
里面动态设置file
属性的class,所以要看接口IFile
?
有多种解决方案,但我目前能想到的最灵活的一种是引入工厂。这里有许多可能的选项,但其中一些是:
- 如果你想在有 Mockito 或 Spock 或类似东西的单元测试中使用它,你可以传递模拟工厂和 return 从它你想要什么。在这种情况下,您甚至不需要像
IFile
/ FileWrapper
这样的自定义接口 / class,如果您使用例如,您可以直接模拟文件 class字节好友.
所以它可能看起来像这样:
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 class,您可以使用 IFile 接口和 FileWrapper 实现,这样工厂看起来像这样:
class FileFactory {
IFile createFile(String path) {
return new FileWrapper(path);
}
}
但其他事情看起来相似 - 您只需要在测试中创建模拟工厂并将其传递给 MyClass 的构造函数。
- 如果你不使用 framework/library,那么你可以像这样自己实现模拟:
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。在测试中,您需要这样的模拟版本:
class MockFile extends File {
// override existing methods and do what you like to do here
}
我有一个 class 创建新的 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
class 来实现此接口并调用 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
class 内部创建,因为 File
构造函数的参数取决于 class 本身的值。
如何在MyClass
里面动态设置file
属性的class,所以要看接口IFile
?
有多种解决方案,但我目前能想到的最灵活的一种是引入工厂。这里有许多可能的选项,但其中一些是:
- 如果你想在有 Mockito 或 Spock 或类似东西的单元测试中使用它,你可以传递模拟工厂和 return 从它你想要什么。在这种情况下,您甚至不需要像
IFile
/FileWrapper
这样的自定义接口 / class,如果您使用例如,您可以直接模拟文件 class字节好友.
所以它可能看起来像这样:
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 class,您可以使用 IFile 接口和 FileWrapper 实现,这样工厂看起来像这样:
class FileFactory {
IFile createFile(String path) {
return new FileWrapper(path);
}
}
但其他事情看起来相似 - 您只需要在测试中创建模拟工厂并将其传递给 MyClass 的构造函数。
- 如果你不使用 framework/library,那么你可以像这样自己实现模拟:
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。在测试中,您需要这样的模拟版本:
class MockFile extends File {
// override existing methods and do what you like to do here
}