TDD by Contract

Test Driven Development (TDD) is a technique at the core of agile software development. This technique has been described by Kent Beck in his book Test Driven Development by Example. The goal of Test Driven Development is to accelerate feedback by conducting the so called TDD cycle. The TDD cycle consists of the three consecutive activities run in a circle: test, code and refactor.

The TDD by Contract cycle shown in the figure below illustrates the rationale of TDD by Contract:
  • Java offers a clear separation of implementation (class level) and design (interface level).
  • The TDD cycle is usualy conducted on the implementation level.
  • But it is also possible - though rarly done - to conduct the TDD cycle on the interface level.
  • On the design level colors indicate result at compile time, i.e. whether the emerging interface and its corresponding interface contract can be compiled (green) or not (red).
  • On the implementation level colors indicate the result at runtime, i.e. whether the focused test on the emerging class runs successful (green) or fails (red).
  • The idea of TDD by Contract is to start the TDD cycle on the design level.
  • As a result we obtain a stable interface with its corresponding interface contract.
  • The interface and its contract form a comprehensive specification of the new type.
  • This specification is an abstract data type entirely written in Java.
  • The implementation of this abstract data tpye is done by conducting the TDD cycle on the implementation level.
As a result we obtain a valid implementation of the previously specified abstract data type. In other words: TDD by Contract encourages us to fully comply with the definition of object oriented programming (OOP) when we build software.

On an design level the TDD cycle comprises the activities Analysis by Contract and Design by Contract and looks like this:

Test

To add a new method to our interface under test, we first write a test using this method. As the method is not declared yet, the test case will not compile (becomes red). This activity is called Analysis by Contract.

Code

We declare the new method in the interface under test so that compiling the test case succeeds (becomes green). In the corresponding interface contract class we add the pre- and postconditions of the new method in order to specify its intended behaviour. As the pre- and postconditions have to use the "vocabulary" of the emerging interface, we might be summoned to declare new methods in the interface in the case the actual "voacubulary" is not rich enough. This recursive activity is depicted in the figure as a small selfreferencing red-green circle. It is an important feature of Design by Contract and leads to consistent and exhaustive and therefore stable interfaces.

Refactor

We clean up our interface under test, i.e. split one method into two in order to adhere to the command query separation principle. We check the result of this refactoring by compiling the test case. When the test case can be successfully compiled (is green again) we can be sure that our refactoring has not broken the interface. In the case of having added a basic query In the corresponding interface contract class we reestablish the accordance of all command postconditions with the fourth of the six principles if necessary, i.e. when having added a new basic query to the interface under test.


Please note that on an interface level testing is done at compile time. Compiling the test case is possible by using the support of modern Java IDEs to create an empty class TimeOfDay which implements the interface TimeOfDaySpec and is auto-completed as the interface grows. By that, running the test case is possible, but all tests usualy fail (become red).

The result of the TDD cycle on the design level is the comprehensive specification of the new type as an abstract data type.

On an implementation level the TDD cycle comprises Implementation by Contract and Testing by Contract and looks like this:

Test

To implement a feature in our class under test, we first write (or if it already exists: focus on) a test using this feature according to its specification given by the contract. This activity is called Testing by Contract. As the feature usualy is not implemented yet, the test will fail (become red) when running JUnit.

Code

We implement the new feature by adding a method or enriching an existing method until the focused test succeeds (becomes green). This activity is called Implementation by Contract.

Refactor

We clean up our implementation, i.e. split a long method into two short ones to ensure that each method has a single responsibility. Please note that such a change has an impact on the interface and interface contract too, so we shortly have to jump back on the design level. Refactoring on the implementation level could enforce refactoring on the design level. C4J's syntax was carefully designed to fully support modern IDE's refactoring functions. We check the result of our refactoring by running the test case. When all tests are green again we can be sure that our refactoring has not broken the class.


Please note that on an implementation level testing is done at runtime. Running the test case must show that all test are green.

The result of the TDD cycle on the implementation level is a valid implementation of the previously specified abstract data type.