Using RhinoMocks to Test Model View Presenter Events with Lambdas and VB / C#

rhino_
This week I had the privilege of leading a custom workshop for a team of developers in Fort Worth, TX. One of the topics that we covered was Writing Testable Code; specifically, we looked at the Model-View-Presenter (MVP) patterns, dependency injection and using RhinoMocks to pull it all together. The one catch was… this group of developers used Visual Basic.

I’ve never shied away from VB. In fact, VB6 with classic ASP is where I cut my programming teeth, and VB.NET was my first move to a true OO language. That being said, I work day in and day out with C#… I’m much more fluent there, and I realized that there were a ton of new features in VB that I wanted to cover that I wasn’t up to par with, fortunately I was able to call on my network of super smart friends and recruit/hire a fellow MVP’er to help me out for the day (thanks Cory for doing that 2 days after your wedding!)

One of the things that tripped me up was using Lambdas in VB. Lambdas are especially important if you are going to use a Mocking framework like RhinoMocks (which I do). Doing Lambdas in VB – especially when I was trying to raise an event from a mock – sort of tripped me up…

First – if you are working with Rhino Mocks, you should go have a look at the excellent Wiki. http://ayende.com/Wiki/Rhino+Mocks+3.5.ashx I find that a lot of people (read: I had) skip the “documentation” since RhinoMocks is so fluent and straight forward, but it’s definitely worth the read!

Second, if you’ve never raised an event in mock from your test in C# here’s the code:

viewMock.Raise(x => x.CalcShippingRequest += null, this, EventArgs.Empty);


and in VB:

view.Raise(Sub(x)AddHandler x.CalcShippingRequest, Nothing, Me, EventArgs.Empty)

It’s the “AddHandler” part that kept tripping me up.. that’s basically += in C#.

Let’s put it all together…

Here’s the IView interface that the aspx page implements. (notice the VB 10 automatic properties)

Public Interface IView

    Property UnitPrice As Decimal

    Property ShippingDistance As Decimal

    Sub DisplayShippingPrice(ByVal price As Decimal)

    Event CalcShippingRequest As EventHandler

End Interface

The Presenter (providing the UI Logic)

Public Class ShippingCalcPresenter

    Dim view As IView

 

    Public Sub New(ByVal view As IView)

        Me.view = view

        AddHandler view.CalcShippingRequest, AddressOf HandleCalc

    End Sub

 

    Private Sub HandleCalc(ByVal Sender As Object, ByVal E As EventArgs)

        Me.view.DisplayShippingPrice(view.ShippingDistance + view.UnitPrice)

    End Sub

End Class

The logic here is simple and contrived… we calculate the Shipping cost based on the Unit price and the Shipping distance (we just add the two… like I said, simple and contrived). Also notice that it’s the event from the view (CalcShippingRequest) that causes the presenter to go in to action, namely, it calls DiplayShippingProce on the view. This could have just as easily been a writable property, but I went with a method instead.

Here is the whole test (VB) AAA Syntax using RhinoMocks

<TestClass()>

Public Class ShippingCalcPresenterTest

 

    <TestMethod()>

    Public Sub ShippingCalcPresenterConstructorTest()

        '*** Assign ***

        'view as a Mock object

        Dim view As IView =

 

        MockRepository.GenerateMock(Of IView)()

 

        'set View values (user input)

        view.Expect(Function(x) x.ShippingDistance)

               .[Return](8).Repeat.Once()

 

        view.Expect(Function(x) x.UnitPrice)

               .[Return](2).Repeat.Once()

 

        'Expect Shipping price of 10 to get set in view

        ' -- Presenter Logic: Price = Distance + Price

        view.Expect(Sub(x) x.DisplayShippingPrice(10)).Repeat.Once()

 

        '  Inject view Mock in to Presenter

        Dim target As New ShippingCalcPresenter(view)

 

        '*** Action ***

        ' Raise event in view... normally this would be a

 

        ‘ user clicking on a button

        view.Raise(Sub(x) AddHandler x.CalcShippingRequest, Nothing, Me, EventArgs.Empty)

 

        '*** Asserttions ***

        view.VerifyAllExpectations()

 

    End Sub

 

End Class


and again, in C#

[TestClass()]

public class ShippingCalcPresenterTest

{

    [TestMethod()]

    public void ShippingCalcPresenterConstructorTest()

    {

        // *** Assign ***

        // view as a Mock object

        IView view =

 

        MockRepository.GenerateMock<IView>();

        // set view values - user input

        view.Expect(x => x.ShippingDistance).Return(8).Repeat.Once();

        view.Expect(x => x.UnitPrice).Return(2).Repeat.Once();

 

        // Expected Shipping price of 10 to get set in view

        // -- Presenter Logic: Price = Distance Price

        view.Expect(x => x.DisplayShippingPrice(10)).Repeat.Once();

 

        // Inject view mock in to Presenter

        ShippingCalcPresenter target = new ShippingCalcPresenter(view);

 

        // *** Action ***

        // Raise event in view - normaly this would be a user

        // clicking on a button

        view.Raise(x => x.CalcShippingRequest += null,

 

       this, EventArgs.Empty);

 

        // *** Assertion ***

        view.VerifyAllExpectations();

    }

 }

First we set up the expectations from the view mock (Assign), then we stuff the view mock in to the presenter (system under test) and then we fire off the event (Act) in the view - this would normally come from a user clicking on something, but in our case, we are simulating the action with a mock. Then we use RhinoMocks VerifyAllExpectations() method to make all of the Assertions that we expected earlier. And there you have it!

Serve hot and enjoy! Happing coding (and testing)!

Photo Credit: Thomas Hawk

http://www.flickr.com/photos/thomashawk/334379948/in/photostream/

About Caleb Jenkins

Caleb Jenkins is the founder and principal mentor for Proaction Mentors, a development and architecture mentoring group. He is probably best known for his work with Microsoft Corporation as a Developer Evangelist and as a technology expert and speaker for the International .NET Association. He currently works with Sabre Airline Solutions as an Agile Coach to mentor agile teams around the globe to be more awesome. He also works as a technical editor and author for Wrox Press and as the host, cameraman and editor for CommunityCast TV. Caleb is well known for his engaging speaking style, depth of knowledge and creative energy. Caleb lives in the Dallas area where he continues to date his beautiful wife and busies himself playing Candy Land and Xbox 360 with their four incredible children. Occasionally he writes curriculum, speaks at conferences, and writes code for silly things like Twitter applications. Eventually he'll post some of the gazillion interviews that he's recorded on CommunityCast or blog at www.developingUX.com You can follow him on twitter (@calebjenkins) and if you're still reading this, then you could also subscribe to his blog RSS feed or sign up to receive updates by email