What Are TDD Scaffolding Tests And Why Do We Need Them?
Three rules govern Test-Driven Development (TDD). The first rule asserts, ‘Only write production code to make a failing unit test pass.‘. Here we have the central tenet of TDD—that we hold off writing behavioural code until we have a unit test demanding the behaviour. And this test must be failing. Only then do we make the test pass by creating the appropriate behaviour in the implementation code.
Frequently, it’s fairly straightforward to produce a failing unit test mandating specific behaviour when we already have the class and method definitions in place.
In the example below (written in C# using XUnit), a SalesTaxSelector
instance throws an appropriate exception when the test calls its Select()
method on a country identifier for which it hasn’t got the corresponding SalesTax
:
Nice. Yet, the only reason we can run this unit test in the first place is because
- We have a
SalesTaxSelector
class, including a default constructor, and SalesTaxSelector
possesses a publicSelect()
method.
That’s great, but how do we get these?
Scaffolding Tests
Let’s start from the top; the most basic requirement—how do we obtain the SalesTaxSelector
class? What possible unit test could we write—and this is the crux—to drive us to define SalesTaxSelector
?
Would this test do it?
Just a minute; that won’t work. The unit test won’t compile; we don’t have a definition for SalesTaxSelector
!
Does this failure to compile not encourage us to create the SalesTaxSelector
class?
It certainly does.
Therefore, once we write a simple class definition, the failing unit test will pass:
You’ll have noticed we only generate a minimum implementation, an empty class definition for SalesTaxSelector
, as specified by Rule 3 of the Three Rules of TDD.
So, to follow the TDD rules, we needed a unit test driving us to implement the SalesTaxSelector
definition—a Scaffolding Test.
The construction industry uses prepared scaffolds to shape poured concrete until it is dry. Once the concrete has hardened, workers remove the scaffolding, and the concrete structure will stand firm.
Similarly, we only require scaffolding tests to establish the necessary programming infrastructure—class or methods—afterwards, they are of no further use to us.
OK, we’ve encountered a scaffolding test for a class. Let’s use the scaffolding unit test approach to produce the SalesTaxSelector
‘s Select()
method:
Once again, the unit test confronts us with a compilation error for the unknown Select()
method, quickly remedied by implementing a basic version on our SalesTaxSelector
:
Again, notice how we represent the Select()
method in the simplest terms:
- No parameters,
- Empty implementation,
- No return value.
The Select()
method’s required implementation specifics will result from further unit tests. Scaffolding tests exist to generate class definitions and public methods.
Furthermore, notice how there are no assertions in Scaffolding Tests. We are not testing for concrete behaviour, as we would normally do with unit tests, but here we wish only to produce classes and methods by counteracting compilation errors with class and method implementations.
Is This Necessary?
On the face of it, defining Scaffolding Tests and then making them pass with trivial class and method definitions appears like make-work. Why bother with scaffolding tests when we understand the class or method we want to define?
I hear such objections regarding scaffolding tests all the time. Here is my reason for keeping them:
Avoiding The Slippery Slope
Moving from a strict application of TDD, where we religiously follow The Three Rules and therefore write all implementation code only when we have a breaking test, is fraught with problems. In my experience, new TDD practitioners who abandon scaffolding tests tend to quickly revert to writing too much code without unit tests to the point where they are no longer practising TDD. Applying the Three Rules consistently and trusting the TDD process, rather than seeking shortcuts, is more likely to lead to TDD success.
Conclusion
Unit tests that drive us to create the classes under test and their public methods are called Scaffolding Tests. This particular type of TDD test helps us stay true to the rule that we should not write code unless we have a failing test. Scaffolding tests do not encode expected program behaviour; therefore, we can remove them once they have done their job.
Leave a Reply
Want to join the discussion?Feel free to contribute!