Choosing a Mocking Framework
Lately I have been getting a bit more serious about Unit Testing, dipping my toe in the Test-Driven Development pool. In reading up on the subject, I came across the concept of using mocking frameworks to dynamically and automatically create stub- and mock- objects. i began looking into the various frameworks available, with an eye to open-source and free-as-in-beer frameworks, as i was not yet sure if i was going to hold my nose and jump all the way into the water, or keep splashing about in the kiddie end of the pool, and saw no sense in spending money on a framework i might not use.
I had been reading a lot of good things about Ayende’s Rhino.Mocks Framework in CoDe Magazine; then recently I added testing guru Roy Osherove’s book The Art of Unit Testing to my O’Reilly Safari books online Bookshelf. Roy uses Rhino.Mocks, henceforth referred to simply as Rhino, almost exclusively as the mocking framework for his examples in the book.
Rhino works just fine with Microsoft’s testing framework MSTest, as well as NUnit. I have not tried it with other frameworks, but I’m sure it works equally well with all, as it is supplied in the form of a .DLL, to which you simply add a reference in your testing project. The current version as of this writing was 3.6, which I downloaded from the Rhino.Mocks download page.
Rhino 3.5+ works with C# and…VB.Net… or does it?
I should mention at this point that all the code to follow uses Rhino’s new-as-of-version-3.5 AAA syntax- that is, Arrange, Act, Assert, which you should Google if you’re not familiar with it, as it is a primary underpinning to the modern TDD approach. I should also mention that this syntax isn’t usable from VB.Net 8, a.k.a. VB.Net 2005. It requires capabilities not added to the language until the 2008 version.
If you look through the Rhino documentation— which is all in C# — for the new syntax, you may wonder if you can use it from VB.Net at all! When you see a line like this in the C# docs:
RegistryObj.Stub(R => R.GetValue(param1, param2, param3)).Return(param3);
You may be forgiven for wondering, “what the heck is that?” “That” is C# syntax for lambda expressions. I won’t go into detail as to what they are— you can look them up on MSDN for the details.
The important part is to understand what’s going on: we have a stub of a Registry wrapper object I wrote to test code that calls the Registry without having to actually touch the real Registry. We have a Rhino-generated stub of a function, which we are setting up to return a specific value when called with specific parameters. And finally, we have a C# lambda expression.
So how does one read that lambda syntax, and what does it mean? According to Microsoft it is read as “R goes to R [etc.]”. As far as this particular usage, it essentially is a way of defining a function in-line, without having to code up an actual function elsewhere.
Lambdas in VB.Net; setting up a stubbed function to return a value
So, you are no doubt asking, “can one do this in VB.Net?” The answer, of course, is “yes you can!” This is what the above call would look like in VB.Net, using VB’s version of the lambda syntax:
RegistryObj.Stub(Function(R) R.GetValue(param1, param2, param3).Return(param3))
You can also specify the type of R, which can help VB.Net deal with some of the weirder Rhino syntax, like this:
RegistryObj.Stub(Function(R as IRegistryWrap) R.GetValue(param1, param2, param3).Return(param3))
And that’s that! Lambdas in VB.Net, sorted! Right? Er, right? No? You mean, you want to be able to use other parts of Rhino? Parts that require even stickier syntax? Demanding, aren’t we?
Calling Rhino’s .AssertWasCalled from VB.Net with a sub
It turns out that there is a really handy extension method that Rhino provides, .AssertWasCalled(), that we would like to be able to call from VB.Net. Calling this method using a lambda and a function works just like the examples above, in both C# and VB.Net. However– there is a “gotcha” when the method you’re calling in the lambda expression is a sub-routine, or, in C#-speak, a “void function”. The problem is that VB.Net does not support lambda expressions without a return value, as detailed by Paul Kimmel on Developer.com.
So, how can we make the call in VB.Net? This will serve as a good example for another difficult-to-call-from-VB.Net method of Rhino. Let’s look at how to call this from C#, as per the documentation:
ClipboardWrapper.AssertWasCalled(C => C.SetFileDropList(fred));
What have we here? This is simply a stubbed version of another one of my wrapper methods, this time wrapping the Clipboard object; we’re calling Rhino’s .AssertWasCalled extension method on it, using a lambda expression, and then setting the void method we need to test as to whether it was called. Very similar to the above code.
So, how do we make the call in VB.Net? The following code would work just fine if .SetFileDropList was a VB function:
ClipboardWrapper.AssertWasCalled(Function (C) C.SetFileDropList(fred))
But, since .SetFileDropList is actually a sub-routine, what we actually get is this error:
If you refer to Paul Kimmel’s article, linked a few lines up, you’ll see that this is the exact error he mentions.
To fix this, we need to use his instructions and create a delegate function. That is, because we can’t create an inline delegate until the next version of VB.Net (2010), we need to actually add a separate wrapper function that returns Nothing, reference it inside the .AssertWasCalled call, and that will satisfy both the compiler and Rhino.Mocks.
Something like this:
Public Sub MyTest()
Function WrapperFunction(ByRef ClipObj As IClipboard) As Object
Why yes, it is a bit ugly; but at least it allows you to keep your tests in VB.Net! And that’s what I promised at the start of this article.
Also, note that I used the type of the Interface in the wrapper function, rather than just declaring it as object, which Kimmel claims will work. I found that the compiler complained about late binding with Option Strict on the ClipObj.SetFileDropList(fred) line unless I declared ClipObj as its interface type, IClipboard.
And that’s it for this article; I will likely have more on using Rhino.Mocks with VB.Net in the near future.