Skip to content

Contract Checks - Once and Only Once for Unit Testing (Part III)

ADMIN | 09-02-2011

I discussed the advantages of incorporating contract checks into one’s unit testing methodology. I thought it would be useful to include an example.

Hexagon pattern 1

In Part I and Part II of this thread, I discussed the advantages of incorporating contract checks into one’s unit testing methodology. I thought it would be useful to include an example, so here it is . . .

To decide on the example, I first thought of the contract check I wanted to write – to check that a sorted list is actually sorted – and then worked my way backwards to figure out the routine that I wanted to write. Sorting routines are already available (qsort, anyone?), so it seemed a little silly to write one of those. Instead, I postulated someone writing a routine that would calculate the permutation needed to sort an array.

Since TDD is all the rage, here’s the test (I’m using C#):

johncode1

Note the validation conditions after the routine has been called: that the permuted values actually are sorted (#2), and that the purported permutation array actually is a permutation (#1, #3, and #4 ). Each of these conditions is suitable as a contract check, since they must always be true, independent of data inputs.

The test-code design complexity issue that I’ve been talking about would come in if we wanted to write a second test, perhaps using a different data-set. In that case, we would either need to write a "design-and-validate" routine that performed phases 2 and 3 together (which is actually almost identical to introducing contract checks), or we would need to pull phase 3 out into another routine, then call it at the end of each test, or exercise some similar architectural cleverness in the test class(es).

In contrast, if we turn the validation conditions into contract checks (by moving them into contract blocks in the routine), then the test now looks like this:

johncode2

 

A lot simpler, eh? The significance is that we just removed a responsibility from the test code – the test code now is only responsible for driving an execution path within the routine; it no longer has responsibility for validating the results. Let me emphasize what I just said, because I didn’t realize it or its significance before I typed it up for this post. In order to solve a once-and-only-once problem, we just removed a responsibility from the test. This would indicate that the true root cause of the complexity is a violation of the Single Responsibility Principle (SRP) by the xUnit test classes – they’re responsible for setup, running, validating and tear-down. Each of the four phases is an independent axis of change; SRP says there should be only one (or was that the Highlander?) :)

Ok, now it’s time for a question: How many of you noticed the bug in my contract check? As a hint, imagine what would happen if the routine were called with this input data instead:

johncode3

 

This is the reason that I wanted to use something related to sorting in my example; it’s very easy to forget the possibility of two identical elements in the list that you’re sorting. (The bug, of course, is that validation 2 should have used "<=", rather than "<".) This is also the heart of why Test2 is a better way of doing things than Test1 – the contract checks in Test2 have a chance of picking up the oversight as a side-effect in another test.  And if the oversight is detected, it is immediately localized to the permutation routine.

This is what I meant in my previous post about "Configuration Coverage" and "(No Need) for Cleverness" – including the validation code in the method gives us a much better chance (than external validation) of picking up and localizing the fact that we missed a subtlety.

Note that such a bug in the contract is important even if the routine works correctly; the fact that the contract is wrong means that the developer didn’t understand everything about the possible input configurations. There are several possible ways the routine could manage duplicate entries; the point is that the choice of behavior should be a conscious decision. Understanding these choices can lead to completely different choices of algorithm and (more importantly) client code behavior and assumptions.

Finding contract bugs is also important in a software-as-an-ecosystem analysis. Recall that the vast majority of defects in a major application like ACIS are not due to "happy path" failures of methods. Instead they come from low probability, hard-to-think-of data configurations (such as repeated list elements in the permutation example) generated in the environment of the application being run millions of times. One way to counteract this environmental dependency and increase robustness is to use provably correct algorithms. The problem is that these proofs rely on correctly understanding exactly those corner-cases which are so hard to think of; missing a case means that the correctness proof could be wrong and the algorithms susceptible to environmental changes.

You might also like...

c138_2_cropped
3 Min read
3D ACIS
We often focus the success of new partners, showcasing how Spatial helped with bringing a new product to market. But...
Large Tolerant  Vertex
3 Min read
3D Software Development Kits
boolean operation Boolean operations on individual bodies are common functions in 3D modeling. While simple in concept,...
3 Min read
3D Software Development Kits
In much the same way as physical design has moved from paper 2D drawings to 3D models in software, so has analysis....
4 Min read
3D InterOp
Part and parcel with model-based engineering is model translation. Because the model is now the specification, accurate...
4 Min read
3D ACIS
We are moving things around in the office here at Spatial to accommodate some new people. As a result, our marketing...
2 Min read
3D ACIS
This article shares a simple architecture which can be used to capture meta-data about the use of ACIS APIs in your...
1 Min read
3D ACIS
How To Create an Ellipsoid using 3D ACIS The 3D analytics supported directly in 3D ACIS include: sphere, block,...
2 Min read
3D ACIS
Basically, there are two priorities when using a software component, particularly a 3D modeling kernel: Does it do what...
1 Min read
3D ACIS
A geometry kernel is a big thing. It’s a huge thing. Maybe even big enough to see from space. By most accounts, even...
1 Min read
3D ACIS
My comrades and I did a performance analysis of Intel’s Hyper Threading Technology (HTT), using thread-safe ACIS and...
3 Min read
3D ACIS
In earlier posts I’ve written a lot about the various approaches to multiprocessing and the potential benefits. What I...
2 Min read
3D ACIS
The challenges of a major software release are not unique to Spatial. And like other organizations, the launch process...
5 Min read
3D ACIS
We’ve known for a long time that the integrity of B-rep data plays a major role in the success of downstream modeling...
3 Min read
3D ACIS
Answer: when it’s a 'HappyPathPoint'.
1 Min read
3D ACIS
To finish up this series of posts; what Gregg's post described happened a few years ago. Since that first team room,...
4 Min read
3D ACIS
This post will discuss two aspects of my favorite programming language, C++:
2 Min read
3D ACIS
Way back in the Dark Ages of the mid-90s, I used to read a newsgroup called comp.lang.c++. You can tell this was the...
7 Min read
3D ACIS
Here’s a subject to which everyone can relate in one way or another: growable arrays. An array is a contiguous memory...