14

I know this is a bit naive. How to unit test this piece of code without giving physical file as input. I am new to mockito and unit testing. So I am not sure. Please help.

public static String fileToString(File file) throws IOException
{
    BufferedReader br = new BufferedReader(new FileReader(file));
    try {
        StringBuilder sb = new StringBuilder();
        String line = br.readLine();

        while (line != null) {
            sb.append(line);
            sb.append("\n");
            line = br.readLine();
        }
        return sb.toString();
    } finally {
        br.close();
    }
}
Raedwald
  • 46,613
  • 43
  • 151
  • 237
user2990315
  • 271
  • 1
  • 5
  • 11
  • 1
    Look at http://stackoverflow.com/questions/17681708/mocking-files-in-java-mock-contents-mockito – AbhinavRanjan Dec 20 '13 at 21:11
  • In the linked examples accepted answer I think he is taking a physical file to test the code. I want to test that without taking a file. – user2990315 Dec 20 '13 at 21:17

4 Answers4

28

You can create a file as part of the test, no need to mock it out.

JUnit does have a nice functionality for creating files used for testing and automatically cleaning them up using the TemporaryFolder rule.

public class MyTestClass {

    @Rule
    public TemporaryFolder folder = new TemporaryFolder();

    @Test
    public void myTest() {
        // this folder gets cleaned up automatically by JUnit
        File file = folder.newFile("someTestFile.txt");

        // populate the file
        // run your test
    }
}
Jeff Storey
  • 56,312
  • 72
  • 233
  • 406
  • 2
    But Isn't that against unit testings best practice to not read physical file? – user2990315 Dec 20 '13 at 21:20
  • No, I would not say so. I don't think it's problematic to use real objects, especially when it makes testing significantly easier. For an object like File that has been heavily tested and used in nearly every Java app (so it probably works pretty well) AND has all sorts of OS specific stuff associated with it, no harm in using a real file. – Jeff Storey Dec 20 '13 at 21:21
  • Don't forget to delete the created file afterwards. – Olivier Faucheux Aug 29 '23 at 10:03
12

You should probably refactor your method. As you realized, a method taking a file as input isn't easily testable. Also, it seems to be static, which doesn't help testability. If you rewrite your method as :

public String fileToString(BufferedReader input) throws IOException

it will be much easier to test. You separate your business logic form the technicalities of reading a file. As I understand it, your business logic is reading a stream and ensuring the line endings are unix style.

If you do that, your method will be testable. You also make it more generic : it can now read from a file, from a URL, or from any kind of stream. Better code, easier to test ...

Guillaume
  • 18,494
  • 8
  • 53
  • 74
  • The method is used only once, that too within the class only. Does it make it a good candidate for making it static. – user2990315 Dec 20 '13 at 21:46
  • Then, that makes it a good candidate to make it private and not test it, not to make it static. Your tests should be agnostic of the implementation, but test the behavior of the unit under test (most of the time, a class). – Guillaume Dec 20 '13 at 21:48
  • I didn't understand your suggestion does using bufferedreader can i mock the bufferedreader input Or is it like i can add content to the buffered reader easily and so I can test it instead of a file input. – user2990315 Dec 20 '13 at 21:49
  • To achieve 100% code coverage is it necessary to test private methods too. – user2990315 Dec 20 '13 at 21:50
  • You can easily create a BufferedReader completely in memory with the help of a StringReader, without the dependency on a File. – Guillaume Dec 20 '13 at 21:51
  • Achieving 100% code coverage is not a reasonable goal. It does not help the quality of your code. If there is a reason to test this specific method, it most probably means that keeping it in the class would break the Single Responsibility Principle. – Guillaume Dec 20 '13 at 21:53
  • I think it need to be static because I am calling from constructor. – user2990315 Dec 20 '13 at 22:15
  • @user2990315 don't perform IO, or indeed any task, in the constructor https://stackoverflow.com/a/24039276/9074921 – Reinis Mazeiks May 08 '20 at 13:35
3

Why do you wanna mock a file? Mocking java.io.File is a bad idea as it has loads of native stuff. I would advice you to ensure that a minimalist text file is available in classpath when the unit tests are run. You can convert this file to text and confirm the output.

Amit Sharma
  • 5,844
  • 5
  • 25
  • 34
  • Thank you. But Isn't that against unit testings best practice to not read physical file? – user2990315 Dec 20 '13 at 21:19
  • Nope. I don't think so. The whole point of unit testing is to write your code in 'units of functionality' and then to test each unit with a small sample input - and verify that you get the right output for this small sample input. – Amit Sharma Dec 20 '13 at 21:21
0

you could use combination of ByteArrayInputStream and BufferedReader class, to make your required file within your code. So there wouldn't be any need to create a real File on your system. What would happen if you don't have enough permission --based of some specific circumstances -- to create a file. On the code below, you create your own desirable content of your file:

public static void main(String a[]){

    String str = "converting to input stream"+
                    "\n and this is second line";
    byte[] content = str.getBytes();
    InputStream is = null;
    BufferedReader bfReader = null;
    try {
        is = new ByteArrayInputStream(content);
        bfReader = new BufferedReader(new InputStreamReader(is));
        String temp = null;
        while((temp = bfReader.readLine()) != null){
            System.out.println(temp);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try{
            if(is != null) is.close();
        } catch (Exception ex){

        }
    }

}
MHK
  • 63
  • 1
  • 8