4.7 Design by Contract


4.7 Design by Contract

Design by contract (DBC) is a method originally proposed by Betrand Meyer [97] for the design of object-oriented and component-oriented systems. The main characteristic of DBC is that classes define their behavior and interplay by contracts. A contract in this context consists essentially of a class invariant and pre- and post-conditions for all methods of the class interface.

While Eiffel, the language preferred by Meyer, explicitly supports contracts, we have to find other ways in Java. There are both commercial Java expansions (e.g., JContract [URL:JContract] and JWAM-Contract [URL: JWAM]) and several free Java expansions (e.g., [URL:IContract]) available for DBC users.[10] One interesting alternative is the use of the contract parts for the construction or expansion of our unit tests:

  • The pre-condition of a method describes the limit of the defined system behavior. It tells us which input data is meaningful for the CUT, thus helping us in our decision of whether or not test X with input Y, Z should still be written. Testing for compliance with the pre-condition itself is not meaningful.

  • A post-condition can be converted directly into an additional assert call. However, the fact that not all CUT-internal matters required for verification are visible from the outside can be a problem. But post-conditions are actually not intended to serve this purpose.

  • The class invariant can serve as an additional post-condition and be treated as such for unit-test purposes.

    Let's use a part of the contract of DictionaryParser as an example:

  • Pre-condition of the constructor: in != null

  • Pre-condition of nextTranslation(): hasNextTranslation() == true

  • Post-condition of nextTranslation(): currentGermanWord != null && currentTranslation != null

We can learn primarily from the pre-conditions which test we will not have to write: no test with null as in parameter and no test for nextGermanWord(), if we have not previously tested hasNextTranslation(). And the following lines could be produced from the post-condition:

 parser.nextTranslation(); assertNotNull(parser.currentGermanWord()); assertNotNull(parser.currentTranslation()); 

We could insert the two assert lines in our test cases after each call of nextTranslation(). On the other hand, it would not make much sense because subsequent asserts would normally implicitly cover both conditions. This is typical because DBC conditions are not allowed to execute operations that change the state of an object and they are therefore often relatively weak in terms of their specificity.

A design paradigm thought to be an antipole of DBC is defensive programming. This paradigm assumes that the pre-conditions of a function or method call are tested by the method itself and that a violation of the precondition leads to a defined error behavior. Fault-tolerant systems normally use this paradigm. The major drawbacks are higher development cost, increased runtime, and a potential veiling of programming errors.

Defensive programming is implicitly assumed in many specifications: developers are expected to respond to wrong input values by an understandable error message instead of an undefined behavior. In contrast to DBC, negative tests for pre-conditions are both meaningful and essential for adequate testing in this case (see Chapter 4, Section 4.5, Error Cases and Exceptions). The general rule here is that defensively developed classes require more isolated unit tests, whereas a contract-based design demands interaction tests [McGregor01, p. 224].

We can see that DBC contracts can serve as inspiration for our unit tests, provided that the contracts were formulated. Method-centered test cases can normally be translated directly into pre- and post-conditions, and vice versa. The big (unanswered) question is therefore, At what point in our test-first development cycle is it meaningful to explicitly specify the contracts? A DBC contract might play its most successful role as an unaware source of ideas during the test-first development, when the developer often has pre-conditions, post-conditions, and invariants in mind without explicitly formulating them. But the actual contracts represent the unit tests themselves.

Explicit contracts are advantageous in any event when they already exist—for example, during subsequent creation of unit tests for existing software (see also Chapter 15, Section 15.1)—and when it is a matter of defining and documenting interfaces for external development teams. If we also use an appropriate DBC expansion, we can reduce the number of unit tests to be written by those that test the contract verified elsewhere.

[10]The assert command available since JDK 1.4 is useful only to a very limited extent, because it does not support subtypes and invariants for DBC.




Unit Testing in Java. How Tests Drive the Code
Unit Testing in Java: How Tests Drive the Code (The Morgan Kaufmann Series in Software Engineering and Programming)
ISBN: 1558608680
EAN: 2147483647
Year: 2003
Pages: 144
Authors: Johannes Link

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net