3

How do you mock file reading/writing via JUnit?

Here is my scenario

MyHandler.java

public abstract class MyHandler {

    private String path = //..path/to/file/here

    public synchronized void writeToFile(String infoText) {
        // Some processing
        // Writing to File Here
        File file = FileUtils.getFile(filepath);
        file.createNewFile();
        // file can't be written, throw FileWriteException
        if (file.canWrite()) {
            FileUtils.writeByteArrayToFile(file, infoText.getBytes(Charsets.UTF_8));
        } else {
            throw new FileWriteException();
        }
    }

    public String readFromFile() {
        // Reading from File here
        String infoText = "";
        File file = new File(path);
        // file can't be read, throw FileReadException
        if (file.canRead()) {
            infoText = FileUtils.readFileToString(file, Charsets.UTF_8);        
        } else {
            throw FileReadException();
        }

        return infoText
    }

}

MyHandlerTest.java

@RunWith(PowerMockRunner.class)
@PrepareForTest({
    MyHandler.class
})
public class MyHandlerTest {

    private static MyHandler handler = null;
    // Some Initialization for JUnit (i.e @Before, @BeforeClass, @After, etc)

    @Test(expected = FileWriteException.class)
    public void writeFileTest() throws Exception {

       handler.writeToFile("Test Write!");

    }

    @Test(expected = FileReadException.class)
    public void readFileTest() throws Exception {

       handler.readFromFile();

    }
}

Given above source, Scenario when file is not writable (write permission not allowed) is OK, However, when i try to do scenario wherein file is not readable (read permission not allowed). It always read the file, i have already tried to modify the file permission on the test code via below

File f = new File("..path/to/file/here");
f.setReadable(false);

However, I did some reading, setReadable() always returns false (failed) when run on Windows machine.

Is there a way to modify the file permission of the target file programmatically in relation to JUnit?

Note

Target source code to test cannot be modified, meaning Myhandler.class is a legacy code which is not to be modified.

lemoncodes
  • 2,371
  • 11
  • 40
  • 66

3 Answers3

2

Instead of relying on the operating system file permissions, use PowerMock to mock FileUtils.getFile(...) and make it return an instance of File (e.g. anonymous sub class) that returns a specific value for canWrite()/canRead().

Mocking static methods with Mockito

Torben
  • 3,805
  • 26
  • 31
  • Ohh i see, i think i got what you mean, have to try it first though. Thanks – lemoncodes Jan 18 '19 at 17:09
  • Note that you should only use PowerMockito as last resort (https://stackoverflow.com/a/30163045/898478). Your case is nowhere near "last resort". – m0skit0 Jan 24 '19 at 11:15
  • @m0skit0 "Power Mock should be used in legacy applications where you cannot change the code which has been given to you" I thought this was exactly that case (or in other words, "what is the alternative approach that you're suggesting"). – Torben Jan 24 '19 at 11:29
  • @Torben I don't see the OP mentioning that `MyHandler` is "legacy code that cannot be changed". You can see my answer for the alternative approach using only Mockito (which btw should also be used only if necessary). – m0skit0 Jan 24 '19 at 11:54
  • It must be your browser then, because OP literally says "Myhandler.class is a legacy code which is not to be modified." – Torben Jan 24 '19 at 12:09
  • @m0skit0 Its on the note, please do note `MyHandler.class` is only a dummy class since i cannot use the actual source here on stackoverflow (its confidential). That being said, that is the reason i created `MyHandler.class` to show what i wanted to show and convey my point. – lemoncodes Jan 28 '19 at 07:40
  • @Torben, thanks, got it working, thanks for the idea. – lemoncodes Jan 28 '19 at 07:42
0

Since Mockito cannot mock static methods, use a File factory instead (or refactor your FileUtils to be a factory), then you can mock it and return a mocked File instance as well, where you can also mock any File methods you want.

So instead of FileUtils.getFile(filepath) you will now have something like FileFactory.getInstance().getFile(filepath) for example, where you can mock getFile(String) method easily.

m0skit0
  • 25,268
  • 11
  • 79
  • 127
  • 1
    Can’t refactor`FileUtils`, its Apache CommonIO library – lemoncodes Jan 18 '19 at 17:08
  • Then as I said use your own factory that internally calls FileUtils and then mock the factory methods to return whatever you want. – m0skit0 Jan 24 '19 at 11:13
  • @m0skit0 How would you pass the custom factory to MyHandler since it's code can not be modified and it accesses FileUtils.getFile statically? – Torben Jan 24 '19 at 11:34
  • @Torben I can't see where OP is saying `MyHandler` cannot be modified. – m0skit0 Jan 24 '19 at 11:55
  • 2
    It's there where OP says "Target source code to test cannot be modified, meaning Myhandler.class is a legacy code which is not to be modified." – Torben Jan 24 '19 at 12:08
0

In jUnit there's a handy rule for scenarios like yours.

public class MyHandlerTest {

    @Rule
    // creates a temp folder that will be removed after each test
    public org.junit.rules.TemporaryFolder folder = new org.junit.rules.TemporaryFolder();

    private MyHandler handler;

    @Before
    public void setUp() throws Exception {
        File file = folder.newFile("myFile.txt");
        // do whatever you need with it - fill with test content and so on.
        handler = new MyHandler(file.getAbsolutePath()); // use the real thing
    }

    // Test whatever behaviour you need with a real file and predefined dataset.
}
Alex
  • 7,460
  • 2
  • 40
  • 51