Coding class invariants is a technique for TacticalTesting
Class invariants are predicates of a class that should always
Common examples are that some field doesn't equal null, or
an integer value is constrained to be within some range.
To code a class invariant, you simply add a method to a class
which tests whether the invariants are true.
If they're not, it reports the problems encountered using
or some other method.
I call this method checkInvariant()
, and it is closely related
Assertions are effectively predicates whose falsehood illustrates
For that reason I suggest that assertions also use ReportBugsSilently
John, it's not clear here and elsewhere in your writing whether you are recommending doing these checks in line, in separate unit tests, or whether you have no preference.
Let me give my view on this. I prefer to do these inline, especially when it's easy to remove them on final deployment. There are several reasons:
- documentation: contracts make it easier to understand a class
- more extensive checking: when leaving the contracts in during functional testing, pilot installations, etc., many more cases will be checked than you can ever code up in unit tests.
- simplicity: in my experience, many contracts are easier to code up than unit tests.
Here's an example from a current project. I had a VisualUIStateCache object that managed a collection of VisualUIStateChange-s. Then it turned out that the changes needed to be connected in a tree. So I gave a change an optional parent and a list of children. A little later one of my unit tests failed, and I could not find the cause. I expected that the tree structure had become inconsistent. So I added two class invariants: (1) the parent of a change has that change as one of its children, and (2) every child of a change has it as its parent. And I made sure that the class invariant was checked at the end of every method that could possible change the state of the object. (By the way, this is the trickiest part in a language that does not support contracts, such as Java.) As it turned out, the unit test still failed. But in 5 minutes I had made sure that the tree structure was not the cause. And -- more importantly -- any future unit test would also automatically test whether the tree structure is kept consistent.
I think class internal consistency checks are best coded as invariants. External, interface consistency (accordance to contract) is more easily tested with separate UnitTest
s. This way, we have both internal and external checking without breaking encapsulation barriers.
I'm of the opinion that the basic building blocks of a program should not be too small, to reduce communication overhead (in LOC) of the basic code, ie no separate methods for nodes of a tree, because the tree is anyway dependent on how its nodes work. So my datastructures often have nontrivial invariants indeed. These invariants should be checked every time
we exit from code that deals with the data structures. If checkInvariant() returns sensible error results, this is an enormous aid in debugging programs (yes, UnitTest
ed programs do have bugs).
I think any system that will be in production use should be in test use with its invariant checking on. External UnitTest
s tend not to catch problems that relate to heavy loads, unpredictable usage patterns, and so on.
'''Implement them in the class itself, and usually only
call them from other class invariants or unit tests. I have no particular argument against calling them from other places though. --JF'''
In most of my programming, the class invariants have seemed to be trivial or uninteresting - or else I haven't thought about them well. The only time we put in a method on the order of isWellFormed was for domain classes about to get sent out for persistence. Most of those classes, containing or representing business rules, had well-formedness constraints worth checking. In your experience, do you find that most or many of your classes have interesting invariants? -- AlistairCockburn
First on the distinction between
trivial or uninteresting
any condition that might fail is interesting. (Just as you code a unit test for anything that might go wrong.)
I found that there are not so many classes that have interesting class invariants. But those that have should definitely be coded up and checked automatically, whether in a unit test or inline.
In my code the invariants often have to do with the relations between objects:
- a BinaryTask? should have exactly two subtasks (and BinaryTask? inherits from Task which can have an arbitrary number).
- the parent of a child of an object should be the object itself
- the list of widgets is always sorted by widgetID.
I've been bitten too often by nasty bugs to leave out such checks. "It couldn't possibly go wrong!" Yes, sure...
Come to think of it, I usually use inline invariants for internal consistency checks, and unit tests for external checks. In my Java code unit tests usually don't have access to the internals of an object. When they need to, I add a method with a name like getParentForTest().
One of my most fruitful class invariants at the moment checks that some cached information is up to date. It makes a copy of shape's bounding box, throws the original away, recalculates it, and compares the new version to the copy of the old. It allows me to use caches, even do tricky things with them, with confidence.
A lot of invariant checks never fail, of course. I still like writing them because I like the mental exercise of writing down all the assumptions a piece of code is making. Making them explicit can help; it is not always obvious from the code that an instance variable can never be null, for example.
Class invariants have a couple of advantages over unit tests. They have access to the private state of the class so can check implementation invariants as well as public behaviour. They can be checked on entry to routines, as pre-conditions - unit tests are only useful for checking post-conditions. They can also check intermediate states.
I'm currently using Microsoft's MFC, which calls the method AssertValid?
(), and uses an ASSERT_VALID macro to call it. -- DaveHarris
Given (1) such tests can be removed easily from (exhaustively tested) production code and (2) source code is formatted in a way that invariant tests do not distract from understanding what's going on, I never ever regretted that I checked an invariant somewhere. On the other hand, after hours of debugging I often regretted NOT having checked an invariant near the (errenous) lines of code starting the trouble. Even if the invariant was found never invalid in unit tests, the effort to write a test for an invariant IMO is a better investment than writing a comment. Finally, thinking about invariants often makes you aware of sections within your code where invariants temporarly not hold tue. This helps to minimize such sections and/or treat them carefully during refactorings. (BTW: If an invariant is trivial, checking it most probably is trivial too and deciding whether you should check it or not will often last longer than writing the actual test.)
Invariant tests are comments. -- JoshuaJuran
Not if you check them statically at compile time.
See also: DaikonInvariantDetector