INTRODUCTION
In my latest article here at VB2TheMax, I showed my code structure for COM+ components (Example of code structure for COM+ component) in preparation for a couple of debug centric articles. Now it’s time for the first of those, but be sure to have the code structure in mind when you read on, because I assume this.
If you expect a variable at a certain place to have a value between 1 and 4 for example, you can express that fact in the code. As you probably know, the Debug
-object in VB has a method called Assert
, which you can use to test your assumptions. Unfortunately, that method will not be compiled into the executable, and that makes the method useless in my opinion.
Well, I’ve seen two good ways to use it:
- Francesco Balena showed in his weekly e-mail from VB2TheMax that you can use
Debug.Assert False
to get a persistent breakpoint in your code, so avoiding problems with theStop
statement. - Peter Morris describes in his book “Advanced Microsoft Visual Basic 6.0, second edition,” which he co-wrote together with others from the company The MandelBrot Set, how you can use
Debug.Assert
to determine if you run the VB code from within VB’s IDE or as an executable.
That’s it as far as I know, but that was of course not what it was supposed to be used for. So what was the real intention?
THE BASIC IDEA
Even if Debug.Assert
had been the perfect tool, where should you use it in your source code? Randomly? Don’t you want to have a plan instead? Yes, you do!
Let’s take a step back. In another programming language called Eiffel, created by Bertrand Meyer, a concept called Design By Contract (DBC) is used to a large extent. (To read more about Eiffel and DBC, read Bertrand Meyer’s book “Object-oriented software construction, second edition.”) The idea of DBC is to set up contracts between consumers and servers. (A consumer uses a class and its methods, a server is the class.) The server says what he expects (Require
) and what he promises (Ensure
). The consumer promises to fulfill the server’s expectations, otherwise the server doesn’t have to fulfill his promises.
Assume that you are the consumer and the Post Office is the server. The contract between you and the Post Office is that you want them to deliver a certain item of snail-mail to the correct person. For you to get that service, they expect to receive the mail before 5 pm today (I live out in the countryside), there should be a valid stamp of the correct value on the envelope, and the correct address must be written on the envelope. (And so on, you get the picture…) If all this is fulfilled, the mail will be delivered correctly to your friend and on time. Hmmm… Perhaps this wasn’t the best example, at least not when it comes to the “company” that takes care of this in Sweden.
(OK, I know. You don’t have to write a contract for this situation. Not even in Sweden.)
What we want is to transfer this contract-thinking to software construction. The software can check itself to see if any nasty bugs have been introduced, and the contracts, which are often implicit, will be made explicit to show which part is responsible for what. That way, you can avoid defensive programming. (In defensive programming everything is checked everywhere. Both the consumer and the server must check the value of the parameter. With DBC, the server can set a requirement and then expect that to be OK. Only the client has to do a test and the server doesn’t. That way the code will be as robust, but with much fewer lines of code. And the number of code lines is often directly correlated to the number of bugs!)
(By the way, it seems that Eiffel is having a renaissance right now, at least when you consider how often it’s mentioned in the Microsoft community compared to earlier. I guess it’s because it has been mentioned as one language that will support the Common Language Runtime (CLR) in .NET, but probably also because it’s a very solid language even though it isn’t new at all.)
To make the whole thing a little bit more concrete, a method called Deliver
in a Mail delivery component could look like this:
Public Sub Deliver(ByRef robjMail As SnailMail)
'Require------------------
If Not OnTime(robjMail.DeliveredAtPostOffice) Then
Err.Raise errSomeCode, "", _
"Consumer didn’t fulfill contract"
End If
If Not Correct(robjMail.Address) Then
Err.Raise errSomeCode, "", _
"Consumer didn’t fulfill contract"
End If
If Not Correct(robjMail.Stamp) Then
Err.Raise errSomeCode, "", _
"Consumer didn’t fulfill contract"
End If
'-------------------------
'Do the real delivery stuff
'...
'Ensure-------------------
If Not CorrectlyDelivered(robjMail) Then
Err.Raise errSomeCode, "", _
"Server didn’t fulfill contract"
End If
'-------------------------
End Sub
This is one possible way of approaching it, but there are obvious problems with it. At first, there will be a lot of overhead during execution. Will you like having all this executed in a component when it has been eagerly tested and used for a long time? Probably not. OK, it’s easy to delete the code. But then you don’t have your contract left or the automatic bug-detection system ready when it’s time to create a new version of the component.
A PROPOSAL
You could use Debug.Assert
above, instead of If
and Err.Raise
. A check would then look something like this:
Debug.Assert Correct(robjMail.Stamp)
(Perhaps you wonder what Correct()
is? Imagine a very smart function that returns True
or False
.)
It’s much cleaner, and it won’t give any overhead except when you run your code in the IDE. On the other hand, there are bugs that won’t be visible when you run the code in the IDE and if that’s the case with your code then you have a problem because your assertions (or contracts) won’t help you.
There are several solutions to this. My proposal is to call a custom sub instead in this manner (this time with another, and commoner, logical expression):
jnskAssert vintTmp > 0, "vintTmp > 0", _
strSource, mobjCtx
The first parameter is the logical expression and the second is the same expression, but as a String
. The third parameter is the source from where the call was made and finally, the last parameter is a reference to the ObjectContext
.
You should have the calls as you saw earlier in sections for Require
and Ensure
. Of course it’s fine to use it in other places in your code too, but this is where to start. So, where is the proper place for Require
and Ensure
in the code structure I presented in Example of code structure for COM+ component? Here is my proposal. Note that the only lines from the code structure article are the On Error Goto ErrorHandler
and the ExitHandler:
lines, but they should be enough to get the picture.
'...
On Error Goto ErrorHandler
'Require------------------
jnskAssert vintTmp > 0, "vintTmp > 0", _
strSource, mobjCtx
'-------------------------
'...
ExitHandler:
'Ensure-------------------
jnskAssert lngReturn >= 4, "lngReturn >= 4", _
strSource, mobjCtx
'-------------------------
'...
The jnskAssert
sub that I keep in an ordinary code module could look something like this:
Public Sub jnskAssert(ByVal vbol As Boolean, _
ByVal vstrStatement As String, _
ByVal vstrSource As String, _
ByVal vobjCtx As ObjectContext)
#If jnskDebug Then
Dim strTmp As String
'Is application configured for
'having assertions checked?
'More info in an upcoming article.
'Do...
If vbol = False Then
strTmp = "Assert broken in " & _
vstrSource & ": " & vstrStatement
'If in IDE, give message as msgbox,
'otherwise in trace and log.
If IsIDE() Then
MsgBox strTmp, vbInformation
Else
'Send trace-message of assert-type.
'More info in an upcoming article.
'Do...
'Write to error-log.
'More info in an upcoming article.
'Do...
End If
End If
#End If
End Sub
Note that the whole sub is wrapped within conditional compilation, to make it possible for you to decide if you like the assertions to be checked during execution at the customer site or not. (I’m aware of a repeating problem in my articles and that is that I’m constantly referring to upcoming articles. Sorry about that, but I must again commit that mistake and state that I will use an upcoming article to show how to make it configurable at runtime if the assertions should be used or not.)
Note also that I have even prefixed my conditional compilation argument. I’ve had problems when merging my code with others in components and used the same name for those arguments. It’s not a big or common problem I guess, but better safe than sorry.
As you saw above, I wanted to know if the code had been executed from the IDE or in an executable. Here is a way to achieve that:
#If jnskDebug Then
Public Function IsIDE() As Boolean
IsIDE = GetModuleHandle("VBA6.DLL") <> 0
End Function
#End If
(And as always in my articles, I skip comments in the header and such. I don’t do that in the real case!)
Even if we set the conditional compilation argument so that the assert code isn’t compiled into the executable (jnskDebug = 0
), there will be a slight overhead since an empty sub will be called. I have written a utility like a preprocessor that works with a copy of the source code and can help with this problem. You can easily write a similar one on your own. You could also wrap all the calls to the sub with conditional compilation, but then the code would be filled with several more statements…
Note that the assertions (such as jnskAssert
) aren’t used instead of if
and Err.Raise
assertions, it’s something else. An assert-call should always be possible to remove from the code. Assert is only intended to proactively detect bugs, not to handle “expected” errors and such.
OUTPUT
With Debug.Assert
you can’t do very much about how the broken expectation is signaled to the developer. You just get a break on the code line with the problematic Debug.Assert
. In my proposal you can decide what should happen. You could for example do approximately the same thing as Debug.Assert
when you’re in the IDE. When you’re running the deployed component, you can send the information as a trace message, write to the application log, and so on, as you saw was intended in the example above.
CLASS INVARIANTS
In Bertrand Meyer’s DBC, there is a concept called class invariants. If we recall the example with the snail-mail earlier, there isn’t anything about social contracts or laws that says we’re not allowed to send narcotic drugs and bombs for example by snail-mail. If someone does try to do that, the contract is not valid and he can’t expect to have the service executed. (Hopefully the police will provide another service for him instead.) All contracts would be extremely long if we had to repeat those things repeatedly. The same goes for classes in software. If a class for a person has a member variable called mintAge
for example, the variable may never have the value of less than 0 and never of more than 125. This condition shouldn’t have to be expressed repeatedly in all the methods of the class. Still, it would be very good to have it checked in every Require
and Ensure
section, to help discover a nasty bug.
How do we arrange for that? A possible solution would be to have a sub like the following in each class:
Private Sub InvariantsCheck _
(ByVal vstrSource As String, _
ByVal vobjCtx As ObjectContext)
#If jnskDebug Then
jnskAssert Len(mstrName) > 0, _
"Len(mstrName) > 0", _
vstrSource, vobjCtx
'More assertions to check.
'...
#End If
End Sub
Then, that sub should be called from the Require
and Ensure
sections in all the Public
methods. The call can be added by a utility to a copy of the code, or you could add it manually of course.
As you saw before, you can of course create complete functions that return True
and False
to provide more advanced logical expressions. Don’t forget to wrap those within the jnskDebug
argument as well.
There is much more to DBC, for example when it comes to implementation inheritance. I will save that discussion until we have VB.NET.
BONUSES
When you run your automatic testing for your components (you do that, right?), you will automatically catch a bug or two just because of the signals from the assertions. Together with comparing the result of the test with the expected result, your assertions are making the net (no, for once net doesn’t mean .NET) finer-meshed.
A second bonus is that I believe the contracts to be great documentation. I’ve seen its use even without the automatic and built-in testing that I’ve discussed here, just because it expresses much of the semantics of the classes and its methods. You can easily build a utility that drags out, for example, the method signatures together with the assertions to create one important part of the documentation of your application.
This documentation is very valuable for public components. The same goes if you work together with someone else in a project. Those contracts express responsibilities in a very natural way. The assertions may also be a handy instrument at design time. They help you to be detailed about the responsibilities of the methods, without writing all the code.
I’ve already talked about assertions when it’s time for a new version of a component, but I’d like to do it again. (No, I’m not being paid by word here. I’m just tedious.) You will be especially fond of your assertions in such a situation. (Have you ever introduced a new bug in the old code when you created a new version? Don’t lie to me!) Most of the assertions should still be valid and you will see at once if you introduce bugs that break the assertions. Very good for productivity and quality!
WHAT ABOUT COM+/MTS?
The title of the article insinuates that it would be especially targeted at COM+, right? You will send the ObjectContext
to jnskAssert
and by doing so several things can be outputted that help understand why an assertion was broken, such as by whom the component was called.
I would also like to give a few small examples that could be useful:
jnskAssert mobjCtx.IsInTransaction, _
"mobjCtx.IsInTransaction", _
strSource, mobjCtx
jnskAssert mobjCtx.IsSecurityEnabled, _
"mobjCtx.IsSecurityEnabled", _
strSource, mobjCtx
Let’s say your customer calls you about a bug in your application that has led to inconsistency in the database. You ask him to send over a trace-log and there you see that a specific component is breaking an assertion since it isn’t using COM+ transactions. When you dig further into it, the customer tells you that the sysadm changed that attribute for the component. His sysadm thought that was good. You can then decide if it’s a bug in your deployment documentation or with the sysadm at your customer site…
ANY TRAPS?
Well, the commonest one is that your assert checks change the execution in any way. Watch out!!! A typical example is that the assert code does some moving around in a recordset, or something like that. Make sure you use a clone in that case.
No matter what, there will be some minor differences when you use assertions and when you don’t since the code isn’t exactly the same. Just take care so you’re not affected. John Robbins discusses this and more in his excellent book “Debugging Applications.”
FINAL THOUGHTS
Should the assertions be left in the compiled code so that they execute at runtime? Well, there is a cost in overhead of course. On the other hand, most often the cost is quite small and it’s great to have the system tell you about when it’s not feeling good and in fact even what is wrong sometimes. Let’s face it. Most development projects are running in Internet time, and there isn’t six months of testing before the components are rolled out.
I leave my assertions in the executable for a couple of weeks after deployment time and after a change. I use a configuration trick so that I can turn off the assert checks during runtime, and almost take away the entire overhead associated with the assertions. If there is need for even better performance, I re-compile without the jnskDebug
flag to get a slimmed version later on, and the calls can also be removed from a copy of the source code by a small utility of mine.
What about ASP and stored procedures in SQL Server? Well, you can use the same basic concept there if you like to. (And you do, right?) I will demonstrate this in an upcoming article.
SUMMARY
Assertions are not a way of skipping other tests. It’s only yet another complement. It’s time for my favorite expression: “There is no silver bullet!”
If you haven’t used assertions before, I hope I have motivated you to try it. If you have used assertions, perhaps you will now go on and take it to the next level.
OK, for several articles now I’ve mentioned tracing. I guess it’s time to discuss some about that in the next article. I have planned to deliver it in three parts. The second part will carry a lot of code, the very same code that I have promised to show you in several articles. Any month now…