When an assert goes off what does it mean? In the codebases I work in, a failing assert doesn’t always indicate that the code is about to fail.
There actually are several conditions that we want to check with asserts. The first three are from Meyer’s Design By Contract:
- Preconditions: conditions that a method requires
- Postconditions: conditions that a piece of method guarantees to be true after executing
- Invariants: class invariants that a method must preserve
I also see two other real-world uses of asserts:
- Catch probable client misbehaviour: These are cases where the method will work, but the arguments are probably incorrect (e.g. salary greater than $1,000,000,000).
- Untested cases: in a situation where not all code-paths have been tested (legacy code) we can put asserts in paths that should work, but aren’t currently tested.
The second set of uses are good, but can make code hard to change because making changes can set off a lot of asserts.
It is useful to distinguish between the cases above. Eiffel does the first three with REQUIRE, ENSURE and INVARIANT. Unfortunately, in plain C++ or C# we just have Assert() which doesn’t give any context. We are moving to using different Assert-like macros in our codebase:
- Assert(): this condition must be true for the code to work. Assert( NULL != pv );
- Expected(): a condition that we expect to be true, but isn’t required. These probably indicate client misbehavior. Expect( cbToCopy < 0x40000000);
- Untested(): Document untested code paths. These can be used to expand test coverage, or indicate that changes are extra-risky.