July 31, 2015

...Learn TDD with Codemanship

Triangulating Your Test Code

While we're triangulating our solutions in TDD, our source code ought to be getting more general with each new test case.

But it's arguably not just the solution that should be getting more general; our test code could probably be generalised, too.

Take a look at this un-generalised code for the first two tests in a TDD'd implementation of a Fibonacci sequence generator:



Jumping in at this point, we see that our solution is still hard-coded. The trick to triangulation is to spot the pattern. The pattern for the first two Fibonacci numbers is that they are the same as their index in the sequence (assuming a zero-based array).

We can generalise our list into a loop that generates the list using the pattern (see Bob Martin's post on the Transformation Priority Premise, or, what I more simply call triangulation patterns).

But we can also generalise our test code into a single parameterised test, using the pattern as the test name, so it reads more like the specification we hope our tests in TDD will become:



Now, because all subequent tests are going to follow the same pattern (we provide an index and check what the expected Fibonacci number is at that index), we could carry on reusing this parameterised test for the rest of the problem.

BUT...

Then we'd have to generalise the name of the test - a key part of our test-driven specification - to the point where every single patterm (every rule) is summarised in one test. I no likey. It's much harder to read, and when a test case fails, it's not entirely clear which rule was broken.

So, what I like to do is keep a bit of duplication in order to have one generalised test for each patterm/rule in the specification.

So, continuing on, I might end up with:



Notice that, although these two test methods are duplication, I've taken the step of refactoring out the duplicated knowledge of how to create and interact with the object being tested. This kind of duplication in test code tends to hurt us most. Many teams report how tight coupling between tests and objects under test led to interfaces being much more expensive to change. So I feel this is a small compromise that aid readability while not sacrificing too much to duplication.





Posted 2 years, 3 months ago on July 31, 2015