Sunday, 16 March 2014

Unit Tests Summary

I have written a lot about Unit Tests in the last few months. It's time to summarize.

Agile

The reason I have talked so much about Unit Tests (recently and in the past) is that I believe it is the core enabling technology of Agile methodologies. Trying to do Agile without Unit Tests is like trying to build a ship without a hull - it just doesn't work. (This is one thing I don't like about Scrum - it does not require any technical procedures like Unit Tests.)

One of the guiding principles of Agile is that the system evolves in response to change. Unit Tests allow that to happen by:
  • detecting bugs immediately after changes are made
  • giving developers the confidence to make changes properly
  • avoiding the need for a lot of regression testing which would kill frequent changes
  • avoiding all of the problems that makes software hard to modify
  • allowing the initial design to start out simpler - avoiding unnecessary complexity (see Reusability Futility)
One important point that is often missed: responding to change is about a lot more than how the software behaves. Just as important is the ability to refactor the design to take advantage of new ideas, knowledge and technologies, without even affecting the external behavior. And, Unit Tests allow you to fix bugs in a way that does not compromise the original design.

Design

There is a lot of evidence that using Unit Tests results in a better initial design.
Unit Tests allow
you to...
take advantage of
new ideas and technologies

Further, without Unit Tests the design of the software will degrade over time (see Why Good Programs Go Bad). With Unit Tests changes can be made properly, so the design does not need to degrade.

Finally, the design can even improve over time, since Unit Tests make it easy to make changes without fear of introducing new bugs. As software is developed and evolves it is inevitable that better ways are found (new algorithms, new hardware, new third-party libraries, etc). Unit Tests allow you to refactor the code to take advantage of new ideas and technologies.

Documentation

There are other advantages to Unit Tests (see What's So Good About 'Em). A major one is that they act as a substitute for (and improvement on) design documentation. Unit Tests have these advantages over written documentation:
  • documents have mistakes - Unit Tests always agree with the code
  • Unit Tests are never out of date as long they are maintained and run often
  • verifiable - just run them to check that they agree with the code
  • more easily written - since developers like coding not writing
  • more easily understood by other developers
  • if not understood you can always step through the code in the debugger

Implementation

Of course, creating Unit Tests is not without its challenges. First of all it can take some time, especially if you need to build test doubles. Here are the major points on creating foolproof tests:
  • the system being tested must have a good, verifiable, modular architecture
  • tests are written by the developer at the same time as the code being tested
  • tests should use knowledge of the implementation (see White Box Testing)
  • but tests should not depend on the implementation (only the interface)
  • tests should be complete (use code reviews and a code coverage tools)
  • tests should only test one concept (see Unit Tests - Best Practice)
  • tests should be short - move set-up and tear-down code to sub-routines
  • tests should be independent (of each other and the environment)
  • use test doubles for any external dependencies that may cause problems
  • use test doubles to simulate errors and other hard to reproduce situations
  • don't use a test double if the emulated module is OK (fast, reliable, etc)
  • tests should be easy to run and fast - and run often
  • use TDD to avoid many of the problems encountered with Unit Tests

No comments:

Post a Comment