Monday, May 07, 2007

Unit Testing and Mock Objects with NMock2 in VB.NET

I've been reading through Jean-Paul Boodhoo's article in Code Magazine "Layered Architecture, Dependency Injection, and Dependency Inversion" and spent some time reading up on the presenter pattern in his original article on microsoft's site in Design patterns on the Model View Presenter.

http://msdn.microsoft.com/msdnmag/issues/06/08/DesignPatterns/

In his article he explains the benefits of using mock objects in your unit tests. Although this post isn't going to go over the details of the article I will cover how I migrated his C# example to VB.NET and other issues I noted in the article.

First, the NMock2 framework is a useful tool for unit testing. Its a tool that can be added to NUnit to enhance your NUnit tests. If you aren't familiar with unit testing with NUnit I suggest starting there.

This is a sample of an NMock2 test found on the design patterns article:
C#:

[Test]
public void ShouldLoadListOfCustomersOnInitialize()
{
mockery = new Mockery();
ICustomerTask mockCustomerTask = mockery.NewMock();
IViewCustomerView mockViewCustomerView =
mockery.NewMock();
ILookupList mockCustomerLookupList = mockery.NewMock();

ViewCustomerPresenter presenter =
new ViewCustomerPresenter(mockViewCustomerView,
mockCustomerTask);

ILookupCollection mockLookupCollection =
mockery.NewMock();

Expect.Once.On(mockCustomerTask).Method(
"GetCustomerList").Will(Return.Value(mockLookupCollection));
Expect.Once.On(mockViewCustomerView).GetProperty(
"CustomerList").Will(Return.Value(mockCustomerLookupList));
Expect.Once.On(mockLookupCollection).Method(
"BindTo").With(mockCustomerLookupList);

presenter.Initialize();
}


Without duplicating the article and taking this line for line I'll just point out a few obvious things. Its a function with an NUnit annotation that tells Nunit it is a test. It creates a mockery class, some objects, and it runs three tests. Now, if you were to run this against your code it would completely pass assuming all your interfaces are created. Why? because the article left out that in the sample code this function also exists in the test code:

C#:

[TearDown]
public void TearDown()
{
mockery.VerifyAllExpectationsHaveBeenMet();
}


This little tidbit gets the mockery class to spit out the results of the tests. Okay, now you have a test that fails... Now lets take a look at the VB.NET version.


<Test()> _
Public Sub ShouldLoadlListOfCustomersOnInitialize()
mockery = New Mockery()

Dim mockCustomerTask As ICustomerTask = mockery.NewMock(GetType(ICustomerTask))
Dim mockViewCustomerView As IViewCustomerView = mockery.NewMock(GetType(IViewCustomerView))
Dim mockCustomerLookupList As ILookupList = mockery.NewMock(GetType(ILookupList))
Dim mockLookupCollection As ILookupCollection = mockery.NewMock(GetType(ILookupCollection))

Expect.Once.On(mockCustomerTask).Method("GetCustomerList").Will(NMock2.Return.Value(mockLookupCollection))
Expect.Once.On(mockViewCustomerView).GetProperty("CustomerList").Will(NMock2.Return.Value(mockCustomerLookupList))
Expect.Once.On(mockLookupCollection).Method("BindTo").With(mockCustomerLookupList)

Dim presenter As ViewCustomerPresenter = New ViewCustomerPresenter(mockViewCustomerView, mockCustomerTask)
presenter.Initialize()


End Sub


You'll notice it's pretty much the same thing. It just has a few syntax changes but the test will work fine. I hope this helps out some VB.NET programmers when trying to get this to work. I found little documentation on it.

Happy Coding!
Jas