To return to the control software of our space shuttle, consider this code:
Function ChangeEnginePower(ByVal niPercent As Integer) As Integer Dim lNewEnginePower As Long Debug.Assert niPercent => -100 And niPercent =< 100 Debug.Assert mnCurrentPower => 0 And mnCurrentPower =< 100 lNewEnginePower = CLng(mnCurrentPower) + niPercent If lNewEnginePower < 0 Or lNewEnginePower > 100 Err.Raise vbObjectError + mgInvalidEnginePower Else mnCurrentPower = lNewEnginePower End If ChangeEnginePower = mnCurrentPower End Sub
Here we want to inform the developer during testing if he or she is attempting to change the engine thrust by an illegal percentage, or if the current engine thrust is illegal. This helps the developer catch bugs during development. However, we also want to program defensively so that if a bug has been created despite our assertion checks during development, it won't cause the engine to explode. The assertion is in addition to some proper argument validation that handles nasty situations such as trying to increase engine thrust beyond 100%. In other words, don't ever let assertions take the place of normal validation.
Defensive programming like the above is dangerous if you don't include the assertion statement. Although using defensive programming to write what might be called nonstop code is important for the prevention of user data loss as a result of program crashes, defensive programming can also have the unfortunate side effect of hiding bugs. Without the assertion statement, a programmer who called the ChangeEnginePower routine with an incorrect argument would not necessarily receive any warning of a problem. Whenever you find yourself programming defensively, think about including an assertion statement.
Explain your assertions
Perhaps the only thing more annoying than finding an assertion statement in another programmer's code and having no idea why it's there is finding a similar assertion statement in your own code. Document your assertions. A simple one- or two-line comment will normally suffice-you don't need to write a dissertation. Some assertions can be the result of quite subtle code dependencies, so in your comment try to clarify why you're asserting something, not just what you're asserting.
Beware of Boolean coercion
The final issue with Debug.Assert is Boolean type coercion. Later in this chapter, we'll look at Visual Basic's automatic type coercion rules and where they can lay nasty traps for you. For now, you can be content with studying the following little enigma:
Dim nTest As Integer nTest = 50 Debug.Assert nTest Debug.Assert Not nTest
You will find that neither of these assertions fire! Strange, but true. The reason has to do with Visual Basic coercing the integer to a Boolean. The first assertion says that nTest = 50, which, because nTest is nonzero, is evaluated to TRUE. The second assertion calculates Not nTest to be -51, which is also nonzero and again evaluated to TRUE.
However, if you compare nTest and Not nTest to the actual value of TRUE (which is -1) as in the following code, only the first assertion fires:
Debug.Assert nTest = True Debug.Assert Not nTest = True
Some final thoughts Debug.Assert is a very powerful tool for bug detection. Used properly, it can catch many bugs automatically, without any human intervention. (See the discussion of an Assertion Sourcerer for a utility that supplements Debug.Assert.) Also see Chapter 1 for further discussion of Debug.Assert.