|Ned Batchelder : Blog | Code | Text | Site|
» Home : Text
Created 11 October 2002, last updated 6 February 2003
Asserts are a valuable tool for testing code. They allow you to verify your understanding of the system you are building. A condition that must be true (an assertion) is tested to see if it is true.
In keeping with the importance of well-defined interfaces, I'm going to separate the caller's view of an assert (its semantics) from its behavior.
The assert facility takes the form of a function call with one argument. (Whether it is actually a function, a macro, or a built-in depends on the language and the implementation). The argument is a condition (an expression) which must be true, for example:
Here is the formal definition of the ASSERT(expr) interface:
The false case is the important one: if the condition is false, all bets are off. There are two reasons for this. One, if the expression was never evaluated, then execution will continue even if the expression is false. Two, how a false assertion should be handled is really a question of the overall behavior of the system, not something the programmer can decide (or count on) where the ASSERT was used.
It is also important to remember that the expression may not be tested at all. It is common for asserts to be removed completely in non-debug builds, so that they add no overhead to the final product.
A note on terminology: the condition (or expression) being tested is the assertion. If the condition is tested and found to be false, that is a failed assertion, although colloquially, people often say the system "has asserted". The phrase "raise an assertion" is also used, paralleling "raise an exception".
So what should happen if the asserted condition is false? Once the semantics are defined as they are above, the behavior can be chosen based on the system as a whole. There are a number of choices:
Once you've chosen a behavior for ASSERT, don't confuse the semantics with the behavior. The semantics remain the same: condition may be tested, true condition continues normally, false condition is undefined.
Of course, asserts are not without their pitfalls. For example, if the asserted expression has side-effects, you can end up with baffling bugs when the asserts are compiled away. This is bad:
Some environments (for example, Microsoft's MFC) accommodate the impulse to write these asserts by providing another macro with the same semantics as ASSERT, except that the expression is guaranteed to be evaluated:
Another trap is that each assert is one more line of code which itself could have bugs. Consider this (real) example:
Not only does the assert not do what it was supposed to do (check that pFoo is NULL), but it accidentally fixes the problem it was meant to detect. If pFoo is wrong (not NULL), this assert will set it to NULL, obscuring the problem. If the ASSERT is compiled away in a release build, the code will start working worse than it did in the debug build.
Asserts should be used only where it is "impossible" for the condition to be false. Don't use them to: