How is design-by-contract related to testing?

Defining clear pre-conditions, post-conditions, and invariants (and automating them in your code via, for example, assertions) helps developers in many ways. First, assertions ensure that bugs are detected early in the production environment. As soon as a contract is violated, the program halts instead of continuing its execution, which is usually a good idea. The error you get from an assertion violation is very specific, and you know precisely what to debug for. This may not be the case without assertions. Imagine a program that performs calculations. The method that does the heavy calculation does not work well with negative numbers. However, instead of defining such a restriction as an explicit pre-condition, the method returns an invalid output if a negative number comes in. This invalid number is then passed to other parts of the system, which may incur other unexpected behavior. Given that the program does not crash per se, it may be hard for the developer to know that the root cause of the problem was a violation of the pre-condition.

Second, pre-conditions, post-conditions, and invariants provide developers with ideas about what to test. As soon as we see the qty > 0 pre-condition, we know this is something to exercise via unit, integration, or system tests. Therefore, contracts do not replace (unit) testing: they complement it. You will see how to use such contracts and write test cases that automatically generate random input data, looking for possible violations.

Third, such explicit contracts make the lives of consumers much easier. The class (or server, if you think of it as a client-server application) does its job as long as its methods are used properly by the consumer (or client). If the client uses the server’s methods so that their pre-conditions hold, the server guarantees that the post-conditions will hold after the method call. In other words, the server makes sure the method delivers what it promises. Suppose a method expects only positive numbers (as a pre-condition) and promises to return only positive numbers (as a post-condition). As a client, if you pass a positive number, you are sure the server will return a positive number and never a negative number. The client, therefore, does not need to check if the return is negative, simplifying its code.

I do not see design-by-contract as a testing practice per se. I see it as more of a design technique. That is also why I include it in the development part of the developer testing workflow (figure 1.4).

NOTE Another benefit of assertions is that they serve as oracles during fuzzing or other intelligent testing. These tools reason about the pre-conditions, post-conditions, and invariants that are clearly expressed in the code and look for ways to break them. If you want to read more about fuzzing,


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *