August 5, 2008

...Learn TDD with Codemanship

Tests Are Instances Of Rules

In test-driven development, we use tests as executable specifications of what is required of the software we create.

For this to work effectively, our tests have to convey the underlying intent. And this requires us to ponder the relationship between a test and a specification.

First of all, what is a specification? Well, in simple terms it's just a rule. A rule that the software must obey. For example, it might be a rule that says that we cannot transfer money from one bank account to the same bank account.

We can test this rule by trying to transfer money from Jill's current account with ID 12345678 to Jill's current account with the same ID.

That is not the rule. The rule applies to every account, not just Jill's. That we cannot transfer money from Jill's current account to the same account is an example of the rule in practice. In much the same that 12345678 is an example (or "instance") of a bank account.

The relationship between types of objects and instances of objects is much the same as exists between rules (and therefore specifications) and tests. Test cases are instances of rules.

Just as we can instantiate a bank account with ID of 12345678 and a balance of 50, so too can we instantiate our rule about transferring funds between the same account where the payer is account 12345678 and the payee is also account 12345678.

In this particular instance, we might expect the software to prevent the transfer from being executed. If we instantiated the rule with payer 12345678 and payee 76564534 we might expect a successful transfer to be completed.

In a test-driven approach, we use the instance to illustrate the rule and serve as the specification ("specification by example").

But sometimes examples don't clearly explain what the rule is - especially when rules are subtle and complex. Which is why it is important when writing tests to make it clear where test data has been substituted for rule variables (like payer and payee) and to express test assertions in terms of rule variables wherever possible.

Some advocates of parameterised testing might wish to go further and dynamically substitute test data in multiple scenarios, cleanly separating code that descrfibes the rule from code that instantiates it.

Whatever your TDD approach, I think the important thing to try and achieve is tests that clearly describe the rule, using carefully chosen test data variable names and thoughtfully-worded assertions to ensure the reader "gets it" as far as the underlying intent is concerned.

Posted 12 years, 10 months ago on August 5, 2008