3

How can I create a class MockFile mimicking java.io.File w.r.t. file read and write? I using everywhere my own methods instead of new FileInputStream(....) and new FileOutputStream(....), so this part is no problem (I always delegate to the appropriate stream). The non-trivila part is the implementation of my MockFileInputStream and MockFileOutputStream in more complicated cases.

There's is no problem, when I first write to a file and then read it, I can simply use a ByteArrayOutputStream and so on. That's simple, but with interleaving reads and writes it can't work. Any better idea than writing my own version of ByteArrayOutputStream?

8
  • 1
    Why are you trying to exchange data between component in the same system via a file? Why not use a temporary file to create a mock file? Commented Jul 27, 2011 at 6:43
  • 2
    You might find your answer [here][1] [1]: stackoverflow.com/questions/3148089/… Commented Jul 27, 2011 at 6:46
  • @Peter Lawrey I'm not trying to exchange data, I'm trying to mimic the way the program works (for a sort of integration test). Using a temporary file is very easy, but not very fast, especially when I need just a few bytes. Commented Aug 1, 2011 at 3:55
  • For passing a few bytes via a temporary file will take about 10 ms. If this is performance problem why are you using files? BTW if you can use tmpfs on unix e.g. /tmp/deleteme.dat, it will take a small fraction of this time because it doesn't actually write to disk, only into memory. Commented Aug 1, 2011 at 6:24
  • 1
    @Peter Lawrey "If this is performance problem why are you using files?" It's no problem in the real run, when such data get written or read from time to time, but in the test it's worse. But I agree that it's probably not worth the hassle, so I'll simply use temporary files, at least for now. Commented Aug 9, 2011 at 5:18

2 Answers 2

4

I would use a real file and a real FileInputStream and FileOutputStream. Otherwise you're just exercising test code: pretty pointless really.

Sign up to request clarification or add additional context in comments.

5 Comments

@EJP This is like "Just deliver to the customer and watch him getting mad. Otherwise you're just exercising test code, pretty pointless really.", isn't it?
@maaartinus you will have to explain that, because I don't get it. The purpose of testing is to test the real code not the test code. Testing the test code is an infinite regress and it doesn't actually achieve the objective, which is indeed to deliver working code to the customer.
@EJP Most tests run actually consist of a mixture of real code and test code (mocks), right? This way we hope to catch the errors in the real code, right? The mocks help to isolate the problem and/or to run the test faster (no slow operations) or cheaping (no real banking operations) or repeatedly (no real nuclear missiles launches), right? So what's wrong with simulating the file system?
There's nothing wrong with simulating a file system. What EJP means (I think) is that constantly using mocks for simple things ends up with so many mocks that you no longer test the real code. It's essentially a slippery slope. Perhaps using an in-memory filesystem (as @peter-lawrey suggests) is the perfect tradeoff. You test real interactions with a file system instead of assuming that you're using the API correctly. See java.dzone.com/news/in-memory-virtual-filesystems for details on this.
@maaartinus What's wrong with it is that it's pointless. It doesn't have any of the risks you mention such as starting Armageddon or tripling the national debt, and the OP specifically mentions that he wants to test interleaving, which is hard enough when the real file system does it, let alone something written only for the purpose.
3

I've created a 'WordCounter' class that counts the words in a file. However, I want to unit test my code and unit tests should not touch the file-system.

So, by refactoring the actual File IO (FileReader) into it's own method (let's face it, the standard Java File IO classes probably work so we don't gain much by testing them) we can test our word-counting logic in isolation.

import static org.junit.Assert.assertEquals;

import java.io.*;

import org.junit.Before;
import org.junit.Test;

public class WordCounterTest {

    public static class WordCounter {

        public int getWordCount(final File file) throws FileNotFoundException {
            return getWordCount(new BufferedReader(new FileReader(file)));
        }

        public int getWordCount(final BufferedReader reader) {
            int wordCount = 0;
            try {
                String line;
                while ((line = reader.readLine()) != null) {
                    wordCount += line.trim().split(" ").length;
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (reader != null) {
                        reader.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return wordCount;
        }
    }

    private static String TEST_CONTENT = "Neque porro quisquam est qui dolorem\n"
            + " ipsum quia dolor sit amet, consectetur, adipisci velit...";

    private WordCounter wordCounter;

    @Before
    public void setUp() {
        wordCounter = new WordCounter();
    }

    @Test
    public void ensureExpectedWordCountIsReturned() {
        assertEquals(14, wordCounter.getWordCount(new BufferedReader(new StringReader(TEST_CONTENT))));
    }
}

EDIT: I should note, if your tests share the same package as your code, you can reduce the visibility of the

public int getWordCount(final BufferedReader reader)

method so your public API only exposes

public int getWordCount(final File file)

1 Comment

I'm afraid you missed the second paragraph of my question... what you did works fine for reading only or for writing only, but I need to test a combination. It's not a unit test, but a test of the cooperation of different parts of the program.

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.