Reputation: 553
I have a class that creates a new File
object:
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
}
}
Now I want to mock out the dependency to File
. So I created an interface IFile
:
public interface IFile {
public String[] list();
public String getPath();
// ...
}
For the actual code I created a FileWrapper
class that implements this interface and calls 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();
}
}
My initial idea was to use IFile
in the constructor of MyClass
(and create a mock implementation for the tests), but this is not possible, because the IFile
can only be created inside the MyClass
class, as the argument of the File
constructor depends on values of the class itself.
How can I dynamically set the class of the file
attribute inside MyClass
, so it depends on the interface IFile
?
Upvotes: 0
Views: 270
Reputation: 519
There are several solutions but the most flexible one I can think of the at the moment is to introduce a factory. There are many possible options here but some of them are:
IFile
/ FileWrapper
, you can mock File class directly if you use e.g. bytebuddy.So it could look like this:
class FileFactory {
File createFile(String path) {
return new File(path);
}
}
class MyClass {
MyClass(FileFactory factory, String path) {
this.file = factory.createFile(path);
}
}
and in the unit test you just need to create mocked FileFactory and pass it as an argument to MyClass's constructor.
class FileFactory {
IFile createFile(String path) {
return new FileWrapper(path);
}
}
but the other things look similar - you just need to create mocked factory in tests and pass it to MyClass's constructor.
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
}
Alternatively, you could get rid of IFile interface and use File class instead. In tests you would need mocked version of it like this:
class MockFile extends File {
// override existing methods and do what you like to do here
}
Upvotes: 1