April 5, 2007

...Learn TDD with Codemanship

Triangulation Patterns

One aspect of test-driven development that folk often struggle to master is the black art of triangulation. Triangulation, according to Kent Beck, is the practice of generalising one test at a time, doing the simplest, least general thing at each step and making the code more general as we go.

For example, if we write a test that the first number in the Fibonacci Series is 0 (i.e., F(0) = 0), then the simplest way to pass that test is to return 0. Our next failing test might be that the second Fibonacci number is 1 ( F(1) = 1 ). We could pass bopth of those tests by return the position F(N) (i.e., if N = 0, return o, if N = 1, return 1, so just return N). In this triangulation, we go from a hard-coded literal return value to a variable (N) return value.

For F(2), that won't work. The third Fibonacci number is not 2, it's 1. At this point, we can introduce a branch, so we return N for N < 2, and 1 for otherwise. We go from a single path of execution to a branch, which is the next least general solution that will pass all three tests.

I notice myself doing these steps - from a hard-coded literal to a variable, from a single path to a branch - very often when doing TDD. Which is highly suggestive of triangulation patterns of a sort.

Other examples are when I go from a single object to a collection of objects. If I have a single test case that expects a mother, Mary, to have a child, John, then I only need one slot to store John in the role "child". If I write another test that expects two children and orders them by date of birth, the role "child" becomes a collection. So single instance -> collection might be another triangulation pattern.

One other example that springs to mind is when I go from a series of IF statements to a loop. This crops up in problems where we have a well-defined sequence or series of solutions. for example, the famous Roman Numeral example. Start with a test that expects the Roman Numeral for 1 = I. Just return I. Test passed. Then for 2 = II, just add a branch for the second case. When we get to 3 = III, it makes sense to loop, concatenating "I" three times. This is useful when we see a series, where each value is related by a set of rules (e.g., N x "I").

Undoubtedly, there are probably more triangulation patterns than the ones that immediately sprang to mind while writing this post. It may well be worth, just as an educational aid, documenting them. Which I imagine I won't get around to, as usual!

Posted 15 years, 8 months ago on April 5, 2007