16

I am working on a project at the moment that generates a table (among other things) based on the contents of a file-system, and in turn does some meta-data modifications on the things it finds. The question is: how should tests be written around this, or set up? Is there an easy way to mock this out? Or should I setup a "sandbox"?

Doc Brown
  • 218,378

5 Answers5

14

As you do always in TDD with external resources: you create one or more interfaces to your filesystem operations and "mock them out". You want to test your "table generator" and your meta-data modification code, not the file system operations itself (most probably you are using ready-made library implementations for accessing the file system).

Doc Brown
  • 218,378
13

Whats wrong with having a "test" file system?

Create a template folder/directory structure which has enough content to test you operations.

During setup of your unit test copy this initial structure (would recommend you ZIP up the template and unzip into your test area). Run your tests. Delete the whole thing during tear down.

The problem with mocking is firstly file systems, OSes and databases which belong to your project do not really qualify as external resources and secondly mocking low level system calls is both time consuming and error prone.

3

I understand your question as "A good/accepted way to test a class that depends on file system operations". I donot assume that you want to test the filesystem of your os.

In order to keep the effort to 'interfaces to your filesystem operations and "mock them out"' as @Doc Brown answer suggested as small as possible it is a good idea to use java binary streams or text reader (or ther equivalent in c# or the programming language you are using) instead of using Files with filenames directly in you tdd-developed class.

Example:

Using java I have implemented a class CsvReader

public class CsvReader {
    private Reader reader;

    public CsvReader(Reader reader) {
        this.reader = reader;
    }
}

For testing I used in memory data like this

String contentOfCsv = "TestColumn1;TestColumn2\n"+
    "value1;value2\n";

CsvReader sut = new CsvReader(java.io.StringReader(contentOfCsv));

or embend testdata into the resources

CsvReader sut = new CsvReader(getClass().getResourceAsStream("/data.csv"));

In production I use the file system

CsvReader sut = new CsvReader(new BufferedReader( new FileReader( "/import/Prices.csv" ) ));

This way my CsvReader does not depend on filesystem but on an abstraction "Reader" where there is an implementation for filesystem.

k3b
  • 7,621
3

This is the kind of thing you definitely need to integration test, as real-world file systems have all kind of strange behavior (like the way Windows won't allow deleting a file if any process, including the deleter, has it open).

So the TDD approach is to write the integration test first (TDD, strictly speaking, doesn't have distinct concepts of 'unit test' and 'integration test'; they are just tests). Quite likely that will be enough; so job done, stop, go home.

If not, there will be some internal complexity that isn't easy to adequately test by arranging files. In which case, you simply take that complexity out, put it in a class, and write unit tests for that class. Quite likely you will find that that common class is usable in the database, xml file, etc. cases too.

In no case would you take the fundamental core of the code you are writing and 'mock' it out in order to write tests that will pass whether or not the unit under test is wrong.

soru
  • 3,655
0

Create a wrapper for the file system operations. In the tests, pass in a mock that implements the same interface as the wrapper. In production, pass in the wrapper.

jhewlett
  • 2,314