14

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"?

13

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).

  • TDD does not recommend mocking the implementation of the unit under test. See (e,g) solnic.eu/2014/05/22/mocking-and-ruby.html – soru Apr 8 '15 at 22:02
  • 1
    @soru: That's not what this answer recommends. It recommends first creating interfaces, then mocking the interface. So you do test the business logic, but not the filesystem interface. – sleske Apr 9 '15 at 0:00
  • 5
    But it sounds like the business logic is defined in terms of files and directories. So the stuff that calls the file system API is the stuff that needs testing. Testing any related non-file business logic does not need mocking; just test it. – soru Apr 9 '15 at 1:35
  • @soru right, so you create a thin layer around files & folders with an interface defined such that all domain-specific operations are on the client side, and the implementation side is trivial enough to be confident it works without unit testing (integration tests will still be required). Similar to the idea of a Humble Dialog in ui code, or using a mock Repository in code that operates on persistent objects. – Jules Feb 19 '16 at 14:46
  • 2
    So effectively we resign from testing the actual class that interacts with file system, database, etc... instead we create a different implementation with the same interface as a mock/stub, but the actual class we leave without any kind of unit testing, because we believe that we cannot unit test it and should do integration testing to test it instead. Is this correct? – Andrew Savinykh Jul 13 '16 at 6:24
10

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.

  • 5
    Mocking out file system operations will create much (!) faster running tests than using a real file system, and if this is more "error prone" is debatable, I would say it depends on the implementation. Nevertheless I think your suggestion is fine for creating automated integration tests (which I typically would do first when not doing TDD). But the OP specficially asked for TDD, and TDD unit tests have to be fast. – Doc Brown Oct 7 '13 at 11:07
  • 1
    I think file systems, if mocked, ideally should have a whole API written and maintained by some group, because you are reinventing the wheel, if you do anything significant with the file system. – Frank Hileman Apr 8 '15 at 21:35
  • 1
    @Doc Brown -- I am sort of assuming he wants to do dir, delete, and rename type operations all of which have edge cases which would be painful to mock. Also on modern hardware unzipping a a few small files into a directory is only a little slower than loading a Java class -- its all IO after all. – James Anderson Apr 9 '15 at 7:46
  • The more I think about the test file system, the more I like it. – Frank Hileman Apr 9 '15 at 20:37
2

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.

  • 2
    The only problem here is that the OP was not speaking of file operations, but file system operations and meta data operations - I guess he meant something like listing all files in a directory, updating some EXIF information in all picture files etc. – Doc Brown Oct 7 '13 at 16:17
  • This is correct. – Kirbinator Oct 7 '13 at 18:37
  • 1
    You can create IDirectoryLister which has a method String[] List(String directory); then FakeDirectoryLister can implement that method by just returning new String[]{".", "..", "foo.bat", "bar.exe"}; – Anders Lindén Dec 10 '13 at 0:23
2

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.

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.

protected by gnat May 2 '16 at 19:25

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

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