In Let your examples flow, Dan North describes how “Don’t Repeat Yourself” (DRY) isn’t necessarily the most important guideline for tests. While I agree with his conclusions, I think the DRY principle is still extremely important for tests.
Dan’s point is that the most important attribute of tests is that they can be read like a story. “Store a page with the text ‘foo’ in the database, simulate a web request, check that the word ‘foo’ is present in the response”. What would happen if “store a page with the text ‘foo’ in the database” was refactored out to a setup method (or a constructor)? The test would dump us into the middle of the story. In medias res is a well know technique for fiction. It’s not as useful for software. So Dan recommends against overapplying “Don’t Repeat Yourself” in tests. I would echo this recommendation, but not quite. Anyone who has maintained a large suite of unit tests knows that duplication can be devastating for maintainability. So I often have custom “assert” methods (
assertNoErrors(ProcessedEntity) is my favorite), I make sure my domain objects are easy to construct from test, and sometimes, I construct classes which are only used to support tests. The important fact is that this reused code is called directly from the flow of the test method and the names of the methods and classes have been chosen carefully to ensure that the tests read well. So: Do follow the DRY principle in tests. But do avoid “magical” setup methods.
Agreed! I often find myself refactoring setup methods to factories called by the individual tests whenever I come across tests that need to do some prep work before they can run. Makes things much easier to grasp when you revisit the test fixtures later.
[Christian Rørdam] - Jul 3, 2008
Agreed! I think it is important that a test method can be read as a sequence of steps, but the steps can be reusable methods. So I do extract the gory details of actually performing the step, but I usually don’t extract a subsequence of steps as a new method. And of course, I try to give the methods as good names as possible, as always.