Skip to end of metadata
Go to start of metadata

Unless you are testing a file utility class, if you access files during your test, you exceeding the scope of the unit that you should be testing.

That statement sums up the principle behind this rule. However, there are more pragmatic reasons to consider as well.

Clarity / Evident Data

Often the motivation for violating this principle is to read test data. Separating test data from the code of your test class makes that test significantly harder to understand and debug.

In his TDD book, Kent Beck alludes to this practice as Evident Data. Show the reader of your test the intent of the code being tested by using clear and understandable data as parameters to your code and expected results in your assert statements. Once test data moves to a separate file, especially in binary formats, the maintenance cost and usefulness of that test becomes problematic.

Speed Of Execution

Reading files is much slower than executing code. Most

Ease of Setup and Brittleness Issues

Unit tests that depend on files being in the right place in order to pass can no longer be executed simply with a test runner and proper classpath. They must be executed in the context of a build which ensures that the files used are in the right place to be read by the test. This raises maintenance concerns, since changes to the test files would cause your unit tests to break in possibly hard-to-understand ways.

Design Considerations

Most of the time what you really care about is streams. Your design should be flexible enough to deal with other sources than files. By accounting for this you will also get a more flexible design.

Recommendations

  • Design all classes that have a legitimate need to read files be coupled to the rest of your codebase via interfaces.
  • In some cases, consider using URLs instead of File classes. URLs have handlers which can be mocked out quite easily.
  • Create a separate, non-Ashcroft integration test suite with tests to cover implementations that read files.
  • No labels

6 Comments

  1. Anonymous

    I disagree that all file I/O in a unit test is wrong. With internationalizd code, I find it basically necessary to store the expected and actual values in a separate configuration file, with each locale having it's own configuration file.

    Truly internationalized code should not need coding changes to support a new locale. This can be realized by adding a new config file for unit testing the new locale.

  2. Anonymous

    Shouldn't those locale-specific files be turned into resource bundles? This way you could use Java's standard RB mechanism and make them available on the classpath (in a JAR) rather than on a specific path.

  3. This is a new file system to manage permissions. It is not very useful on its own. You need to load other modules

  4. Anonymous

    What if I'm test-driving web page templates? Typically I'm reading the (Velocity) template under test from the file system. Would Mr Ashcroft tell me to stop doing that? I /think/ I'm writing a good unit test.

  5. You may need to think again and change all your tests to load these templates as resources using same classloader used to load test classes. This way you won't be dependent on relative or absolute path on the file system and most likely eliminate some redundant properties. See above comments.

  6. Anonymous

    I don't agree that files are bad in all cases. I think they make sense in a significant minority of cases. What if the data is large ? or binary eg. pictures ?
    I use a drive letter (Drive R) created using SUBST (in Windows) for the repository root, and so tests can use an absolute path to access their data and it will always be there. eg. R:\tests\ImageTest\pic.jpg.
    R: can be created on any test machine at any physical directory location. Cheers, GaryMc http://jesq.blogspot.com/