9

I'm trying to write unit tests for a method that takes a String filename, then opens the file and reads from it. So, to test that method, I thought about writing a file, then calling my method. However, in the build farm, it is not possible to write files arbitrarily to disk. Is there a standard way to "simulate" having a real file in my unit test?

5
  • What about using the temporary directory? see File#createTempFile Commented Aug 7, 2012 at 16:01
  • Is the file dynamic? Is the file name dynamic? What is expected from the stream? Commented Aug 7, 2012 at 16:06
  • createTempFile throws SecurityException, just my problem... Commented Aug 7, 2012 at 16:11
  • Just a configuration file for some object: I want to put together some configuration, and have the class read it, it's not very dynamic at all. Commented Aug 7, 2012 at 16:11
  • There's a related / duplicate question here: stackoverflow.com/questions/6840303/… Commented Mar 6, 2014 at 18:30

4 Answers 4

19

I've found that Mockito and Powermock are a good combination for this. Actually there's a blog post with a sample, where the File-class's constructor is mocked for testing purposes. Here's also a small example I threw together:

public class ClassToTest
{
    public void openFile(String fileName)
    {
        File f = new File(fileName);
        if(!f.exists())
        {
            throw new RuntimeException("File not found!");
        }
    }
}

Testing with Mockito + Powermock:

@RunWith(PowerMockRunner.class)
@PrepareForTest(ClassToTest.class)
public class FileTest
{
    @Test
    public void testFile() throws Exception
    {
        //Set up a mocked File-object
        File mockedFile = Mockito.mock(File.class);
        Mockito.when(mockedFile.exists()).thenReturn(true);

        //Trap constructor calls to return the mocked File-object
        PowerMockito.whenNew(File.class).withParameterTypes(String.class).withArguments(Matchers.anyString()).thenReturn(mockedFile);

        //Do the test
        ClassToTest classToTest = new ClassToTest();
        classToTest.openFile("testfile.txt");

        //Verify that the File was created and the exists-method of the mock was called
        PowerMockito.verifyNew(File.class).withArguments("testfile.txt");
        Mockito.verify(mockedFile).exists();
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

The linked blog post is no longer available.
3

If you use JUnit, there is the TemporaryFolder. Files are deleted after the test. Example given on the page:

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

  @Test
  public void testUsingTempFolder() throws IOException {
      File createdFile= folder.newFile("myfile.txt");
      File createdFolder= folder.newFolder("subfolder");
      // ...
     }
 }

However, I have also used it for testing my Android class read/write capabilities like:

    [...]
    pw = new PrintWriter(folder.getAbsolutePath() + '/' + filename);
    pw.println(data);

2 Comments

Nice approach, the thing is that if you use a physical file, your test become an integration one, it is not a unit test anymore
If you use the unit test framework's ability to provide test files and directories, you don't actually know if it's creating a physical file -- nor do you care.
0

How about using mocked stream classes, which override the real ones (like BufferredReader, File) completely (meaning all methods or all methods you use)? The data can be saved as an array of bytes, for example, in some singleton, if they are to be used between different test classes.

1 Comment

I'll look into that. It would allow me to avoid going to disk, which would make the tests faster?
-2

This is highly frowned upon:

The smallest amount of testable code. Often a single method/function, sans the use of other methods or classes. Fast! Thousands of unit tests can run in ten seconds or less! A unit test NEVER uses:

  • a database
  • an app server (or server of any kind)
  • file/network I/O or file system;
  • another application;
  • the console (System.out, System.err, etc.)
  • logging
  • most other classes (exceptions include DTO's, String, Integer, mocks and maybe a few others). "

Source

If you must read from a file, have a test file pre-generated that all unit tests read from. There is no need to write anything to disk.

1 Comment

I don't understand this answer -- the question concerned testing a method that does file I/O. The test itself was explicitly intended NOT to do any file I/O. This is a perfectly acceptable case for a unit test.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.