January 6, 2015

...Learn TDD with Codemanship

Example Random Parameterised Unit Tests With JCheck

Pairing with a client developer yesterday led to some code that you might find interesting.

My pairing partner was new to parameterised testing, and wanted to see how one could refactor and triangulate our way from traditional unit tests with a single input and expected output to a parameterised test with multiple hardcoded inputs and expected outputs, and then on to something that could give us exhaustive coverage with as little code as we could.

Sticking to a very familiar problem, we went through the FizzBuzz kata and eventually ended up with this.

You'll have to excuse my basic knowledge of JCheck, but I think this test fixture will cover pretty much every possible value from 1 to 100.

Notable firstly is that we've generalised our tests, but not directly in the assertions, but rather in the constraints on the generated test input values. This is peculiar to this kind of problem, probably. But we would certainly expect these constraints to correspond to the pre-condition for each test.

Note, too, the duplication we chose to leave in the test code. We could refactor this into an even more general test method, but when we tried the code started to become obfuscated, with a very long test method name describing all of the FizzBuzz rules.

So we rolled back to what I think is a pretty readable pattern for test code: one test per rule.

One more thing I'd like to point out before I'm done; if we look at the actual implementation, we see that the algorithm used is different to logic we used in the test code when we generalised it.

It's pretty unavoidable in randomising test inputs that our test code has to use algorithms to calculate expected results, unless we want to construct tables of every possible permutation.

The danger is that we use the same algorithms as the implementation. I get a much warmer, fuzzier feeling knowing that both our tests and our implementation arrived at matching results by different routes. (And, of course, the ultimate crime is to use the implementation itself to calculate the expected results - that way lies madness!)

MASSIVE PLUG: If this sort of fun and larks interest you, you might want to check out my new Advanced Unit Testing course.

Posted 2 years, 11 months ago on January 6, 2015