An assertion is a statement that asserts that something is true. In VB6 you add assertions like this:
Debug.Assert 0 < x
If the statement is true then the program continues as if nothing happened but if it is not then the program will stop at that line. Unfortunately VB6 has a particularly weak form of assertions, they are only executed when the code is running in the debugger. This means that they have no effect at all in the compiled program. Don't let this stop you from using them, after all this is a feature that doesn't exist at all in many common programming languages.
If you really need the assertions to be tested in the compiled program you can do something like this:
If Not(0 < x) Then Debug.Assert False
Err.Raise ErrorCodes.AssertionFailed End If
Now the program will stop at the failure when in the IDE and raise an error when running compiled. If you plan to use this technique in more than a few places it would make sense to declare a subroutine to do it so as to reduce clutter:
Assertions and Design By Contract
Public Sub Assert(IsTrue as Boolean) If Not IsTrue Then
Debug.Assert False
Err.Raise ErrorCodes.AssertionFailed End If
End Sub
then instead of writing Debug.Assert you write: Assert 0 < x
Assertions can be used to implement a form of design by contract1. Add assertions at the beginning of each routine that assert something about the values of the arguments to the routine and about the values of any relevant module or global variables. For instance a routine that takes a single integer argument that must be greater than zero would have an assertion of the same form as the one shown above. If it is called with a zero argument the program will halt on the line with the assertion. You can also add assertions at the exit of the routine that specify the allowed values of the return value or any side effects.
Assertions differ from explicit validation in that they do not raise errors or allow for the program to take action if the assertion fails. This is not necessarily a weakness of the assertion concept, it is central to the different ways that assertions and validation checks are used.
Assertions are used to do several things:
• They specify the contract that must be followed by the calling and called code,
• They assist in debugging by halting execution at the earliest point at which it is known that something is wrong. Correctly written assertions catch errors long before they cause the program to blow up.
Assertions generally assist in finding logic errors during the development of a program, validation, on the other hand, is usually intended to trap poor input either from a human being or other external unreliable source. Programs are generally written so that input that fails validation does not cause the program to fail, instead the validation error is reported to a higher authority and corrective action taken. If an assertion fails it normally means that two internal parts of the program failed to agree on the terms of the contract that both were expected to comply with. If the calling routine sends a negative number where a positive one is expected and that number is not provided by the user no amount of validation will allow the program to recover so raising an error is pointless. In languages such as C failed assertions cause the program to halt and emit a stack trace but VB simply stops when running in the IDE. In VB assertions have no effect in compiled code.
Combined with comprehensive tests, assertions are a great aid to writing correct programs. Assertions can also take the place of certain types of comments. Comments that describe the allowed range of values that an argument is allowed to have are better written as assertions because they are then explicit statements in the program that are actually checked. In VB you must exercise the program in the IDE to get the benefit of the assertions, this is a minor inconvenience.
Effective Programming
Assertions also help ensure that a program remains correct as new code is added and bugs are fixed. Imagine a subroutine that calculates the equivalent conductance of a radiator due to convection (don't worry if the physics is unfamilar):
Public Function ConvectionConductance(Byval T1 as Double, Byval T2 as Double) as Double
ConvectionConductance = 100 * Area * (T2 - T1)^0.25 End Sub
Now it so happens, if you know the physics, that conductance is always a non-negative number regardless of the relationship between the temperatures T1 and T2. This function, however, makes the assumption that T1 is always greater than, or equal to, T2. This assumption might be perfectly reasonable for program in question but it is a restriction nonetheless so it should be made part of the contract between this routine and its callers:
Public Function ConvectionConductance(Byval T1 as Double, Byval T2 as Double) as Double
Debug.Assert T2 < T1
ConvectionConductance = 100 * Area * (T2 - T1)^0.25 End Sub
It is also a good a idea to assert something about the results of a routine:
Public Function ConvectionConductance(Byval T1 as Double, Byval T2 as Double) as Double
Debug.Assert T2 <= T1
ConvectionConductance = 100 * Area * (T2 - T1)^0.25 Debug.Assert 0 <= ConvectionConductance
End Sub
In this particular case it looks as if the assertion on the result is worthless because if the precondition2 is satisfied it is obvious by inspection that the postcondition3 must also be satisfied. In real life the code between the precondition4 and postcondition5 assertions is usually much more complicated and might include a number of calls to functions that are not under the control of the person who created the function. In such cases the postcondition6 should be specified even if it appears to be a waste of time because it both guards against the introduction of bugs and informs other programmers of the contract that the function is supposed to comply with.