Design by Contract – Is It Useful Without Unit Testing?

design-by-contractunit testing

I read Bertrand Meyer's paper on design by contract yesterday and it is not very clear for me what is the relationship between DbC and testing, since it appears that without testing I cannot be certain all the assertions were hit at some point.

Consider a square root function that has preconditions defined that verify the function is never called with a negative number.

Somewhere in the application code the function is used, consider something like

if(n >= 0) {
   // a bunch of code
}
else {
   // some code
   square_root(n)
}

The programmer that added the call to square_root(n) did not consider that it would be called with a negative number (evidently a bug was just introduced).

Obviously, if I run the program in a way that it reaches this branch, with assertions on, we would get an exception reported and we could identify the problem immediately. If we make sure to hit every single call of square_root with any possible category of value it could take, and we don't get any assertions broken, then we know our program is correct and, in production code, we could safely run it without assertions and assume it keeps its reliability properties.

But this has the implication that the code in question needs to be tested. And so, in my example above of the square root function being called with a negative number, if we don't test it extensively in a way that every assertion is hit, and if we switch off assertions in production, then I may end up with a horrible production bug.

And so, it appears that DbC without testing is somewhat useless to ensure my program is correct and reliable. On other hand, if I write unit tests to make sure the square root function is called only with the right kind of arguments, then what's the point of the assertions if my tests are doing all the work of making sure my code is right?

So, what I'm trying to understand here is what is the relationship between DbC and unit testing, and how unit testing does not make DbC meaningless since my tests are a substitute for the assertions in the code.

What am I missing?

Best Answer

DbC isn't necessarily realized with "assertions which are deactivated in production". Assumed there are no insane performance requirements, contract checks should stay in the code when it is operated in production. That way, they will help to detect bugs which have slipped through the net of unit tests, and may become apparent first time with production data. A violated contract will make the program "crash early", and not sweep the issue under the rug, which could otherwise lead to nasty, subsequent faults with a much harder-to-find root cause.

But even if one uses only debug level assertions for DbC, they can be helpful to find bugs not covered by unit tests - specificially during integration tests and end-to-end tests. The latter kind of tests will usually not point you to the specific unit which has a defect. When an end-to-end test fails, in a suffciently complex system, you may know that something went wrong, but have a really hard time to find the root cause for it.

The situation changes a lot when your code contains assertions or contract checks: those will make it more likely that a problem will show up a lot earlier, ideally nearby the code section which is responsible for it (it might not be directly the buggy code itself, but I know from first-hand experience that such assertions can make it a lot easier to spot the heart of such an issue).

Related Topic