What? Helper methods? Whenever I see a class called Helper in code I think that the developer who wrote it wasn’t willing to take the time to discover what objects were really responsible for those behaviors. Although we would like to believe otherwise, in reality, there is no benevolent helper class in the world that floats around and provides utility services, or at least we shouldn’t think about modeling it that way in our code.
In other words, I think that oftentimes helper methods and helper classes in production code is a code smell that is hiding entities that have yet to be discovered in the system. The good news is that we can use this code smell to help us discover what the missing entities are and thereby enrich the design of the system.
However, I have a very different opinion about helper methods when used in test code versus production code. Test code is different than production code. It serves a different purpose and unlike the object-oriented programs that I write, which endeavor to become models of whatever it is that I’m building, my test code is really scenarios for exercising those models in the ways that I intend to use them.
The intention is not to build a model and test code but rather to exercise the model through test scenarios. Helper methods for creating in maintaining test scenarios are entirely valid and so I use them quite a bit when writing unit tests.
Refactoring is a major part of doing software development. Refactoring code is like editing prose. It’s always better to work with something rather than trying to start with a blank page. Any writer will tell you that. I believe it’s true for software development as well. When you understand how to do refactoring efficiently and effectively, it turns out to be a valuable technique for emerging designs safely and efficiently. But it does mean that I spend a significant portion of my time refactoring code. In addition to refactoring my code, I also spend time refactoring in my tests. In fact, I tend to spend more time refactoring my tests then I spend refactoring my code.
When I’m refactoring my tests I’m doing a different activity than when I’m refactoring my code. When I refactor code, I’m usually doing it to improve the design and I draw on a variety of principles, design patterns, practices, and other techniques to create understandable and maintainable designs. When I am refactoring my tests I’m generally doing it to remove redundancy. This has a lot to do with how I create my tests in the first place.
Let’s say I’m creating a system that requires users to register and log in before they may access an online forum. Every test that exercises the behavior of the forum must first register and login users. The set up for this may involve many steps and be quite complex but we can encapsulate this into a helper method.
If there’s a common set up or tear down that all test need in a particular test class then we can put this in a test initialize or tests finalize method that every unit testing framework supports, although the syntax is slightly different between frameworks. However, we often need to do additional set up in some tests that other tests don’t need and this is where helper methods come in.
First of all, think about what these additional steps accomplish for the test scenarios and put that common behavior in a helper method owned by the test and shared among multiple test methods. Give the helper method an intention revealing name that describes what it does.
Creating helper test methods not only removes redundancy by taking common scenario elements and putting them in a single place, but it also gives us the opportunity to give those common steps I name and thereby documenting the purpose which helps our tests read more like specifications so it’s more understandable to others.
Note: This blog post is based on one of the “Seven Strategies…” sections in my book, Beyond Legacy Code: Nine Practices to Extend the Life (and Value) of Your Software.
Previous Post: « Instrument Your Tests
Next Post: Show What’s Important »