8.2. Assertions

 < Free Open Study > 

An assertion is code that's used during development usually a routine or macro that allows a program to check itself as it runs. When an assertion is true, that means everything is operating as expected. When it's false, that means it has detected an unexpected error in the code. For example, if the system assumes that a customerinformation file will never have more than 50,000 records, the program might contain an assertion that the number of records is less than or equal to 50,000. As long as the number of records is less than or equal to 50,000, the assertion will be silent. If it encounters more than 50,000 records, however, it will loudly "assert" that an error is in the program.

Assertions are especially useful in large, complicated programs and in high-reliability programs. They enable programmers to more quickly flush out mismatched interface assumptions, errors that creep in when code is modified, and so on.


An assertion usually takes two arguments: a boolean expression that describes the assumption that's supposed to be true, and a message to display if it isn't. Here's what a Java assertion would look like if the variable denominator were expected to be nonzero:

Java Example of an Assertion
assert denominator != 0 : "denominator is unexpectedly equal to 0.";

This assertion asserts that denominator is not equal to 0. The first argument, denominator != 0, is a boolean expression that evaluates to true or false. The second argument is a message to print if the first argument is false that is, if the assertion is false.

Use assertions to document assumptions made in the code and to flush out unexpected conditions. Assertions can be used to check assumptions like these:

  • That an input parameter's value falls within its expected range (or an output parameter's value does)

  • That a file or stream is open (or closed) when a routine begins executing (or when it ends executing)

  • That a file or stream is at the beginning (or end) when a routine begins executing (or when it ends executing)

  • That a file or stream is open for read-only, write-only, or both read and write

  • That the value of an input-only variable is not changed by a routine

  • That a pointer is non-null

  • That an array or other container passed into a routine can contain at least X number of data elements

  • That a table has been initialized to contain real values

  • That a container is empty (or full) when a routine begins executing (or when it finishes)

  • That the results from a highly optimized, complicated routine match the results from a slower but clearly written routine

Of course, these are just the basics, and your own routines will contain many more specific assumptions that you can document using assertions.

Normally, you don't want users to see assertion messages in production code; assertions are primarily for use during development and maintenance. Assertions are normally compiled into the code at development time and compiled out of the code for production. During development, assertions flush out contradictory assumptions, unexpected conditions, bad values passed to routines, and so on. During production, they can be compiled out of the code so that the assertions don't degrade system performance.

Building Your Own Assertion Mechanism

Many languages have built-in support for assertions, including C++, Java, and Microsoft Visual Basic. If your language doesn't directly support assertion routines, they are easy to write. The standard C++ assert macro doesn't provide for text messages. Here's an example of an improved ASSERT implemented as a C++ macro:

Cross-Reference

Building your own assertion routine is a good example of programming "into" a language rather than just programming "in" a language. For more details on this distinction, see Section 34.4, "Program into Your Language, Not in It."


C++ Example of an Assertion Macro
#define ASSERT( condition, message ) {       \    if ( !(condition) ) {                     \       LogError( "Assertion failed: ",        \           #condition, message );             \       exit( EXIT_FAILURE );                  \    }                                         \ }

Guidelines for Using Assertions

Here are some guidelines for using assertions:

Use error-handling code for conditions you expect to occur; use assertions for conditions that should never occur Assertions check for conditions that should never occur. Error-handling code checks for off-nominal circumstances that might not occur very often, but that have been anticipated by the programmer who wrote the code and that need to be handled by the production code. Error handling typically checks for bad input data; assertions check for bugs in the code.

If error-handling code is used to address an anomalous condition, the error handling will enable the program to respond to the error gracefully. If an assertion is fired for an anomalous condition, the corrective action is not merely to handle an error gracefully the corrective action is to change the program's source code, recompile, and release a new version of the software.

A good way to think of assertions is as executable documentation you can't rely on them to make the code work, but they can document assumptions more actively than program-language comments can.

Avoid putting executable code into assertions Putting code into an assertion raises the possibility that the compiler will eliminate the code when you turn off the assertions. Suppose you have an assertion like this:

Visual Basic Example of a Dangerous Use of an Assertion
Debug.Assert( PerformAction() ) ' Couldn't perform action

Cross-Reference

You could view this as one of many problems associated with putting multiple statements on one line. For more examples, see "Using Only One Statement per Line" in Section 31.5.


The problem with this code is that, if you don't compile the assertions, you don't compile the code that performs the action. Put executable statements on their own lines, assign the results to status variables, and test the status variables instead. Here's an example of a safe use of an assertion:

Visual Basic Example of a Safe Use of an Assertion
actionPerformed = PerformAction() Debug.Assert( actionPerformed ) ' Couldn't perform action

Use assertions to document and verify preconditions and postconditions Preconditions and postconditions are part of an approach to program design and development known as "design by contract" (Meyer 1997). When preconditions and postconditions are used, each routine or class forms a contract with the rest of the program.

Further Reading

For much more on preconditions and postconditions, see Object-Oriented Software Construction (Meyer 1997).


Preconditions are the properties that the client code of a routine or class promises will be true before it calls the routine or instantiates the object. Preconditions are the client code's obligations to the code it calls.

Postconditions are the properties that the routine or class promises will be true when it concludes executing. Postconditions are the routine's or class's obligations to the code that uses it.

Assertions are a useful tool for documenting preconditions and postconditions. Comments could be used to document preconditions and postconditions, but, unlike comments, assertions can check dynamically whether the preconditions and postconditions are true.

In the following example, assertions are used to document the preconditions and postcondition of the Velocity routine.

Visual Basic Example of Using Assertions to Document Preconditions and Postconditions
Private Function Velocity ( _    ByVal latitude As Single, _    ByVal longitude As Single, _    ByVal elevation As Single _    ) As Single    ' Preconditions    Debug.Assert ( -90 <= latitude And latitude <= 90 )    Debug.Assert ( 0 <= longitude And longitude < 360 )    Debug.Assert ( -500 <= elevation And elevation <= 75000 )    ...    ' Postconditions Debug.Assert ( 0 <= returnVelocity And returnVelocity <= 600 )    ' return value    Velocity = returnVelocity End Function

If the variables latitude, longitude, and elevation were coming from an external source, invalid values should be checked and handled by error-handling code rather than by assertions. If the variables are coming from a trusted, internal source, however, and the routine's design is based on the assumption that these values will be within their valid ranges, then assertions are appropriate.

For highly robust code, assert and then handle the error anyway For any given error condition, a routine will generally use either an assertion or error-handling code, but not both. Some experts argue that only one kind is needed (Meyer 1997).

Cross-Reference

For more on robustness, see "Robustness vs. Correctness" in Section 8.3, later in this chapter.


But real-world programs and projects tend to be too messy to rely solely on assertions. On a large, long-lasting system, different parts might be designed by different designers over a period of 5 10 years or more. The designers will be separated in time, across numerous versions. Their designs will focus on different technologies at different points in the system's development. The designers will be separated geographically, especially if parts of the system are acquired from external sources. Programmers will have worked to different coding standards at different points in the system's lifetime. On a large development team, some programmers will inevitably be more conscientious than others and some parts of the code will be reviewed more rigorously than other parts of the code. Some programmers will unit test their code more thoroughly than others. With test teams working across different geographic regions and subject to business pressures that result in test coverage that varies with each release, you can't count on comprehensive, system-level regression testing, either.

In such circumstances, both assertions and error-handling code might be used to address the same error. In the source code for Microsoft Word, for example, conditions that should always be true are asserted, but such errors are also handled by error-handling code in case the assertion fails. For extremely large, complex, long-lived applications like Word, assertions are valuable because they help to flush out as many development-time errors as possible. But the application is so complex (millions of lines of code) and has gone through so many generations of modification that it isn't realistic to assume that every conceivable error will be detected and corrected before the software ships, and so errors must be handled in the production version of the system as well.

Here's an example of how that might work in the Velocity example:

Visual Basic Example of Using Assertions to Document Preconditions and Postconditions
Private Function Velocity ( _    ByRef latitude As Single, _    ByRef longitude As Single, _    ByRef elevation As Single _    ) As Single    ' Preconditions    Debug.Assert ( -90 <= latitude And latitude <= 90 )       <-- 1    Debug.Assert ( 0 <= longitude And longitude < 360 )         |    Debug.Assert ( -500 <= elevation And elevation <= 75000 )       <-- 1    ...    ' Sanitize input data. Values should be within the ranges asserted above,    ' but if a value is not within its valid range, it will be changed to the    ' closest legal value    If ( latitude < -90 ) Then       <-- 2       latitude = -90                  |    ElseIf ( latitude > 90 ) Then      |       latitude = 90                   |    End If                             |    If ( longitude < 0 ) Then          |       longitude = 0                   |    ElseIf ( longitude > 360 ) Then       <-- 2    ... 

(1)Here is assertion code.

(2)Here is the code that handles bad input data at run time.

 < Free Open Study > 


Code Complete
Code Complete: A Practical Handbook of Software Construction, Second Edition
ISBN: 0735619670
EAN: 2147483647
Year: 2003
Pages: 334

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net