Mocking Files in Java - Mock Contents - Mockito

Mocking Files in Java - Mock Contents - Mockito

You seem to be after contradictory goals. On the one hand, you're trying to avoid writing data to disk, which isn't a bad goal in tests. On the other, you're trying to test your I/O-handling class, which means you'll be working with system utilities that assume that your File will work with native calls. As such, here's my guidance:

  • Don't try to mock a File. Just don't. Too many native things depend on it.
  • If you can, split your I/O-handling code into the half that opens a File and turns it into a Reader, and the half that parses HTML out of the Reader.
  • At that point, you don't need a mock at all--just construct a StringReader to simulate the data source.
  • While that handles your unit tests pretty well, you may also want to write an integration test that uses a temporary file and ensure that it reads right. (Thanks Brice for adding that tip!)

Don't be afraid to refactor your class to make testing easier, as here:

class YourClass {
public int method(File file) {
// do everything here, which is why it requires a mock
}
}

class YourRefactoredClass {
public int method(File file) {
return methodForTest(file.getName(), file.isFile(),
file.isAbsolute(), new FileReader(file));
}

/** For testing only. */
int methodForTest(
String name, boolean isFile, boolean isAbsolute, Reader fileContents) {
// actually do the calculation here
}
}

class YourTest {
@Test public int methodShouldParseBadHtml() {
YourRefactoredClass yrc = new YourRefactoredClass();
assertEquals(42, yrc.methodForTest(
"bad.html", true, false, new StringReader(badHTMLText));
}
}

At this point the logic in method is so straightforward it's not worth testing,
and the logic in methodForTest is so easy to access that you can test it heavily.

Mockito - Mocking behaviour of a File

In this sort of situation, I would use physical files for testing the component and not rely on a mocking framework. As fge mentions it may be easier plus you don't have to worry about any incorrect assumptions you may make of your mock.

For instance, if you rely upon File#listFiles() you may have your mock return a fixed list of Files, however, the order they are returned in is not guaranteed - a fact you may only discover when you run your code on a different platform.

I would consider using JUnit's TemporaryFolder rule to help you set up the file and directory structure you need for your test, e.g.:

public class DummyFileClassTest {
@Rule
public TemporaryFolder folder = new TemporaryFolder();

@Test
public void someMethod() {
// given
final File file1 = folder.newFile("myfile1.txt");
final File file2 = folder.newFile("myfile2.txt");

... etc...
}
}

The rule should clean up any created files and directories when the test completes.

mock - creating new files (Java)

inside ur code extract below into a local variable

File f= new File(path);

also in the test code

@PrepareForTest(File.class ) //Here instead of File it should be the class where new file is created, i.e. YourClass.class

i.e.

@PrepareForTest(ClassYoureCreatingTheFileInstanceIn.class)

now below code should work

File myFile = PowerMockito.mock(File.class);
PowerMockito.whenNew(File.class).withAnyArguments().thenReturn(myFile);
Mockito.when(myFile.exists()).thenReturn(true);
Mockito.when(myFile.mkdirs()).thenReturn(true);

Any way to mock File.exist()/ File.isFile() method with Mockito?

You can mock file class behavior as follows.

 File mockedFile = Mockito.mock(File.class);
Mockito.when(mockedFile.exists()).thenReturn(true);

This tutorial should help.

Edit...

You need to make the method testable. Method should take in the files it is operating on. The mocked objects should be passed as parameters. For example,

public void deleteMyFile(List<File> m_files){

for(int i = 0; i < m_files.size(); i++) {
File m_file = m_files.get(i);
if(m_file.exists()) {
FileUtils.deleteQuietly(m_file);
if(m_file.isFile()) {
m_log.error("Deleting file " + m_file.getName() +" fails");
throw new ServiceUnavailableException("Not successfully delete the file " + m_file.getName());
} else {
m_log.info("Successfully delete the file " + m_file.getName());
}
}
}
}

And the test code would look like this.

@Test
public void test(){
File mockedFile = Mockito.mock(File.class);
Mockito.when(mockedFile.exists()).thenReturn(true);
Mockito.when(mockedFile.isFile()).thenReturn(true);
List<File> files = new ArrayList<>();
files.add(mockedFile);
MyTestClass myTestClass = new MyTestClass();
myTestClass.deleteMyFile(files);
}

How to mock a Files static method with Mockito

When you write something with tdd and have troubles consider it as signal of bad design. You don't need to mock static fields or find some tricky lib for do it. Instead of doing this make entity that represent filesystem and place all methods related to file operations to this class. With this refactor your code will be like that:

class UtilClass { //util classes are bad don't do it
private final FileSystem fileSystem;

public UtilClass(FileSystem fileSystem) {
this.fileSystem = fileSystem;
}

public void createDirectories(final Path root, final Scaffolding scaffolding) throws FileAlreadyExistsException {
if (fileSystem.exists(root)) {
throw new FileAlreadyExistsException("Root directory " + root.toString() + " already exists.");
} else {
fileSystem.createDirectories(root);
// Create directories from the scaffolding object
}

interface FileSystem {

boolean exists(Path path);

void createDirectories(Path path);
}

and test class

class UtilClassTest {

@Test(expected = FileAlreadyExistsException.class)
public void shouldThrowExceptionWhenRootPathExists() {
FileSystem mockFileSystem = Mockito.mock(FileSystem.class);
Mockito.when(mockFileSystem.exists(anyPath())).return(true);
UtilClass util = new UtilClass(mockFileSystem);
util.createDirectories(mock(Path.class), mock(Scaffolding.class))
}
}

In your code outside tests replace mock to implementation.

class FileSystemImpl implements FileSystem {

boolean exists(Path path){
return Files.exists(path);
}

createDirectories(Path path){
return Files.createDirectories(path);
}

}

and you don't need to touch file system in tests or mock static fields.



Related Topics



Leave a reply



Submit