1

I have this class:

public class MultiFilesIterator implements IMultiFilesMerger {

    private RandomAccessFile randomAccessFile;
    private int currentFileIndex = 0;
    int filesCount;

    @Inject
    @Singleton
    public MultiFilesIterator() {
        this.randomAccessFile = setRandomAccessFile(0);
        IFileUtils fileUtils = new FileUtils();
        this.filesCount = fileUtils.getCountOfSimilarNamedFilesFromDir(Constants.RT_UPDATE_INPUT_PATH);
    }

    @VisibleForTesting
    public MultiFilesIterator(IFileUtils fileUtils) {
        this.randomAccessFile = setRandomAccessFile(0);
        this.filesCount = fileUtils.getCountOfSimilarNamedFilesFromDir(Constants.RT_UPDATE_INPUT_PATH);
    }

    @Override
    public String getNextLine() {
        String line;
        try {
            line = randomAccessFile.readLine();
        } catch (IOException ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }

        if (line == null) {
            currentFileIndex++;
            if (currentFileIndex < filesCount) {
                randomAccessFile = setRandomAccessFile(currentFileIndex);
            }
        }
        return line;
    }

    @Override
    public String getFirstLine() {
        setRandomAccessFile(0);
        return getNextLine();
    }


    private RandomAccessFile setRandomAccessFile(int i) {
        try {
            return new RandomAccessFile(Constants.RT_UPDATE_INPUT_PATH + i, "r");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

}

It abstracts few files with name "A0", "A1" .. with an iterator that iterate them all as if they were one big file.

I want to test that given two files, the getNxtLine() moves from A0 to A1 eventually.

I thought to add a test ctor and send it a list of RandomAccessFile mocks.

How would you abstract it?

I thought to write an inMemory implementation and test it.

public class MultiFilesIteratorInMemory implements IMultiFilesMerger {

    private List<RandomAccessFile> randomAccessFiles;
    private RandomAccessFile randomAccessFile;
    private int currentFileIndex = 0;
    int filesCount;

    @Inject
    @Singleton
    public MultiFilesIteratorInMemory(IFileUtils fileUtils, List<RandomAccessFile> randomAccessFiles) {
        this.randomAccessFiles = randomAccessFiles;
        this.randomAccessFile = setRandomAccessFile(0);
        this.filesCount = fileUtils.getCountOfSimilarNamedFilesFromDir(Constants.RT_UPDATE_INPUT_PATH);
    }

    @Override
    public String getNextLine() {
        String line;
        try {
            line = randomAccessFile.readLine();
        } catch (IOException ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }

        if (line == null) {
            currentFileIndex++;
            if (currentFileIndex < filesCount) {
                randomAccessFile = setRandomAccessFile(currentFileIndex);
            }
        }
        return line;
    }

    @Override
    public String getFirstLine() {
        setRandomAccessFile(0);
        return getNextLine();
    }


    private RandomAccessFile setRandomAccessFile(int i) {
            return randomAccessFiles.get(i);
    }
}

and

public class MultiFilesIteratorInMemoryTest {
    @Before
    public void setUp() throws Exception {

    }

    @After
    public void tearDown() throws Exception {

    }

    @Test
    public void getNextLine() throws Exception {

    }

    @Test
    public void timeFrameIsSplitIntoTwoFilesReturnAllRelevantRecords() throws Exception {

        RandomAccessFile randomAccessFile1 = mock(RandomAccessFile.class);
        RandomAccessFile randomAccessFile2 = mock(RandomAccessFile.class);
        IFileUtils fileUtils = mock(IFileUtils.class);

        when(randomAccessFile1.readLine()).thenReturn("first file content").thenReturn(null);
        when(randomAccessFile2.readLine()).thenReturn("second file content").thenReturn(null);

        IMultiFilesMerger multiFilesMerger = new MultiFilesIteratorInMemory(fileUtils, ImmutableList.of(randomAccessFile1, randomAccessFile2));
    }

}

but then I mock a concrete class. Why is this bad?

2

I'd avoid implementing the class twice.

I would simply test the original implementation i.e. MultiFilesIterator class with real files dedicated for the tests.

The class is simple enough to test directly, and seems to be designed to specifically work with files, so I think the extra level of abstraction is redundant here.

This would also keep your testing decoupled from your implementation, so if you ever choose to use say FileReader class instead of RandomAccessFile, your tests would remain the same.

  • ׳so if you ever choose to use say FileReader class instead of RandomAccessFile, your tests would remain the same.׳ Then I need to wrap RandomAccessFile and make the wrapper implement an interface. That makes the code cumbersome. – Elad Benda Apr 25 '16 at 10:21
  • What I mean is that you can ignore the internal implementation of MultiFile reader, so your tests wouldn't care what you've used internally - FileReader or RandomAccessFile. If you do choose to change your implementation you would re-implement MultiFilesIterator with the chosen file reading class, no need to wrap the actual classes you use to access files. – Rafi Goldfarb Apr 25 '16 at 15:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.