Follow @RoyOsherove on Twitter

The role of helper frameworks in your tests

As you create tests for a system that gets bigger and bigger, so do the tests become bigger and more complex. One of the problems that start to occur is that you test code starts getting riddled with various object-creation code. For example, you're testing a complex business object's state, to make it just right for testing an exception that should be thrown. That sort of creation can take many lines of code and might repeat itself in other tests too. This causes several symptoms:

First, your tests are now much less readable than they should be. Tests should be readble so they can serve as part of the overall API documentation which can then be used by members of your team to familierize themselves with your code.

Second, you're breaking one of the first rules of good code : "Don't repeat yourself". Duplication in your tests is just as bad as duplication in your prodcution code. In this case there's going to be lots of duplication in the creation and state-setting of your to-be-tested business objects.

By refactoring, we can start removing this duplication into various helper methods that will be laid down within our test fixture. However, this still has the effect of making your test fixture do more than one thing (it's both a test fixture and a helper class now). Additional refactoring eventually leads us to create a helper class with specific helper methods (usually static) for the business objects under test.

This "Pattern" of helper methods and factories for your unit tests is not new. It's been called several names ("ObjectMother" - PDF , for example) and eventually, most developers that implement unit tests arrive at the same conculsion, one way or the other.

Assuming that most projects will have at least one such helper class, we can and should begin to realize and think about our helper factories and classes for testing as pure frameworks on which to build our tests. These frameworks can later be used to run not only our developer tests, but our customer tests as well (using FIT,  for example). Given the "organic" nature in which an agile software project usually grows, these frameworks are grown, and not pre-designed. As such, we should pay special attention to them as they are created and nurtured, and to make sure that once one has benn "born" we start thinking of it as a service to out tests, and to future tests as well. For example, there may be a good point in time where these factories and helpers will need to become a separate assembly in our tests, so that it can be re-used in other testing scenarios. We can also re-use these frameworks later adding more generality to them and thus making them easier to use with customer tests.

These frameworks can be very small in small applications (one-two utility classes) or very big on large ones (hierarchical, categorized and refactored classes for each object type, test type and so on). One of the most important things to keep in mind as you build such a framework is to re0use existing methods to their smallest parts. That is, make your code inside those methods repeat itself exactly once. The minute ou break this rule you're heading down a path where future changes in your business objects might lead to big lengthy and painful changes in your helpers. If you keep the DRY rule, you'll most likely need to change very little creation code in order to support such changes. Even the acto of callin "new" on an object should be repeated only once and refactored into a method that will be re-used by all other helper methods. That way if your object loses its default constructor, for example, you need only change one simple method to factor the change into your helper framework.

"Buy first, ask questions later"

Get an MSN alert whenever this blog updates