Test Driven Development
Introduction
Excerpt Our aim is to explore the philosophy of TDD. The concept and practice of writing tests first and then developing a system from the inputs to the tests and the results from the tests is awesome but I won't call it revolutionary. I will explain why once we've got the concepts under our belts.
Collateral
...
Should I care about testing
If you're reading this then I'm assuming you have an interest in unit testing and delivering quality systems. Why have I used the term system and not software? It's because the principle of TDD can be applied to the development of software and infrastructure. But can we apply it to the concepts of design and architecture? That's a little more complex as these artifacts are logical constructs that cannot be executed. Your architecture and designs are made manifest through your software and hardware.
So what about unit tests or to be more accurate applying TDD at different levels within the software. A unit test is a test applied to a unit of code (function, class or object). Applying TDD at a unit level is not the same as applying it at a system level (or the user acceptance level). Examples of unit testing tools are JUnit, NUnit, JTiger, TestNG, and Mockito etc. Examples of what I'm calling system level testing tools are FitNesse, Cucumber and Gherkin, Selenium and SpecFlow etc.
So what's the difference between unit testing and system (acceptance) level testing? Consider the following example - you've been tasked with providing the infrastructure for a high speed transportation system (a train).
Current state of tracks
- The current tracks allow for rolling stock to travel at up to 160mph
- The maximum rolling stack length is 12 x 23.9m
- The maximum tonnage the tracks can carry at low and high speed must be defined by the rail standards authority
- The tracks, clips and bolts are of a specific tensile strength
- The switching circuitry operates at the specific capacity
Target state for tracks
- The new tracks must allow for rolling stock to travel at up to 220mph
- Rolling stock will tilt at high speed through bends
- The maximum rolling stack length is 12 x 23.9m
- The maximum tonnage the tracks can carry at low and high speed must be newly defined by the rail standards authority
- The tracks, clips and bolts are of a specific tensile strength to cope with the higher speed rolling stock
- The switching circuitry operates at the specific capacity
In order to ensure that the new rolling stock can traverse over the new tracks at the new desired speed, each element (component) must be verified as being able to carry the rolling stock at the new desired speed. In other words each element making up the track system must be separately unit tested to verify their individual quality. Once the components have been assembled into a complete track system, a series of integration/system level tests must be executed to verify that the rolling stock can actually get to their destinations within a specified set of requirements. So from a software perspective unit level testing with tools like JUnit/NUnit verify the quality of the components in a system, and tools like FitNesse verify that the system is built according the requirements (user acceptance testing), in other words you have built what I asked for (more of an end to end testing strategy).
History
When I was studying for my degree I can clearly remember the tutor at the time introducing the concepts of Jackson diagrams and DFDs. During the course of learning these techniques the tutor addressed the idea of specifying a system like this
Drawio | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
The idea was really simple. Workout the input values, specify the expected output values, and functionally decompose the process element using Jackson type elements. Today we've replaced the phrase "functionally decompose" with the phrase "refactor".
Look, I don't want you to get it into your head that I'm pushing for us to go back to structured techniques. I've been an OO developer since 1989 when Borland release its first C++ compiler, Turbo C++. What I am showing you is that the concept of test first, code second isn't new.
What is different is the incremental iterative approach to building out the landscape. Also, in the structured model, you would attempt to define all the input and output values before any coding could be done. Not so with TDD. Instead, everything is done incrementally and iteratively. Even the identification of the tests.
Is it possible to perform unit testing with tools like FitNesse?
Yes it is but why would you? You would actually be using the wrong tool for the particular job. It would be like using a hammer to put a screw in the wall. Yes it would work and the results would be the same, but the approach at that level of development would be cumbersome.