This week I am going to stick with the theme set in my previous blog post and continue on the topic of testing code. Last time I talked about the importance of keeping tests focused. This time I am going to introduce the concept of testing seams and how they can be used in a testing suite.
As an introduction, a testing seam is a place in the code that allows you divert the normal execution flow. The same way that a seam in a cloth is a place where you can tear the cloth apart into two separate pieces and then stitch them back together, a testing seam is a place in the code where you can separate two components that would normally be connected during normal execution.
As a way of example look at the following class:
Component has two responsibilities. It does its own thing and then it calls the delegate instance to do something else. Imagine you want to test the
Component class. As we discussed last time unit tests should be kept tightly focused so they are fast to run and enable you to quickly focus on possible bugs if they fail. In this particular example that probably means that we only want to test the logic that belongs to the
Component class while ignoring the
Delegate class which should have its own tests.
Unfortunately, the way the
Component class is currently structured makes that impossible. There is only one way to instantiate a
Component—via its only constructor—and that will result in an instance of the actual
Delegate being created. That means when the tests invoke
doTheThing() the actual
doTheDelegateThing() will also be invoked which is not what we want here.
Fortunately in this particular example this is easy to fix:
Now production code can simply ensure the existing behaviour is maintained by passing an instance of
Delegate when constructing
new Component(new Delegate()) and at the same time tests can pass a test double in place of the Delegate that behaves in a predictable, configurable way by doing
new Component(new DelegateDouble()). This way
Component logic can be isolated from the
Delegate logic in the tests.
This should serve as an introduction to the topic, but there are many kinds of testing seams that are intended for different use cases and that are more or less difficult to implement. In addition there is the issue of adding testing seams to existing, complex code that was not initially designed with testability in mind which is a problem with its own unique sets of challenges. For additional information I recommend reading Working Effectively with Legacy Code by Michael Feathers.
That’s it for this week. I’ll be writing some more on the topic of testing soon to cover other useful concepts and techniques.
If you enjoyed the read, drop us a comment below or share the article, follow us on Twitter or subscribe to our #MetaBeers newsletter. Before you go, grab a PDF of the article, and let us know if it’s time we worked together.