Search code examples
javaunit-testingnio

Should I create wrapper for java.nio.Files for Unit test purpose?


I am trying to write unit test for below class without creating real file

public class TempFileWritter{
    public String writeToTempFile(byte[] value) throws IOException {
        Path tempFile = Files.createTempFile(dir, prefix, suffix);
        Files.write(tempFile, value);
        return tempFile.toAbsolutePath().toString();
    }
}

I am using Mockito that cannot mock static method. So that my current solution is writing a wrapper class for java.nio.Files class and the I can inject it to the my class as below:

MyAppFileUtils class:

public class MyAppFileUtils {

    public void write(Path file, byte[] value) throws IOException {
        Files.write(file, value);
    }

    public Path createTempFile(Path dir, String prefix, String suffix) throws IOException {
        return Files.createTempFile(dir, prefix, suffix);
    }
}

The modified class is:

public class TempFileWritter{
    MyAppFileUtils fileUtils;
    public void setFileUtils(MyAppFileUtils fileUtils) {
        this.fileUtils = fileUtils;
    }
    public String writeToTempFile(byte[] value) throws IOException {
        Path tempFile = fileUtils.createTempFile(dir, prefix, suffix);
        fileUtils.write(tempFile, value);
        return tempFile.toAbsolutePath().toString();
    }
}

Someone agrue that creating class MyAppFileUtils is redundant because it don't do anything except calling method in class java.nio.Files. Could you give me some advices about that?


Solution

  • Use JimFs an in-memory implementation of the Java 7 filesystem API. You will need to make sure that your code allows for alternative filesystem implementations, but it should result in more extendable code and its much more preferable to mocking.

    Let's say your class looks like

    public class TempFileWriter {
    
        private final Path tempDir;
        private final String prefix;
        private final String suffix;
    
        public TempFileWriter(Path tempDir, String prefix, String suffix) {
            this.tempDir = tempDir;
            this.prefix = prefix;
            this.suffix = suffix;
        }
    
        public Path writeToTempFile(byte[] value) throws IOException {
            Path tempFile = Files.createTempFile(tempDir, prefix, suffix);
            Files.write(tempFile, value);
            return tempFile;
        }
    }
    

    Your unit test could be

    @Test
    public void testCreateTempFile() throws IOException {
        FileSystem fs = Jimfs.newFileSystem();
    
        Path tempDir = fs.getPath("mytempdir");
        String prefix = "myprefix";
        String suffix = ".tmp";
    
        byte[] data = new byte[1];
        data[0] = 0x66;
    
        TempFileWriter tempFileWriter = new TempFileWriter(tempDir, prefix, suffix);
        tempFileWriter.writeToTempFile(data);
    
        Files.list(tempDir).forEach(path -> {
            String fileName = path.getName(0).toString();
            assertTrue(fileName.startsWith(prefix));
            assertTrue(fileName.endsWith(suffix));
            assertTrue(Files.readAllBytes(path)[0] == 0x66)
        });
    }
    

    Your code is now more extendable, because you can use different filesytem implementations. Your unit test is improved because you don’t need to mock anything.