Using Rhino Mocks To Unit Test Events on Interfaces

code, tdd 0 comments suggest edit

Rhino I am working on some code using the Model View Presenter pattern for an article I am writing. I am using an event based approach based on the work that Fowler did. For the sake of this discussion, here is an example of a simplified view interface.

public interface IView
{
    event EventHandler Load;
}

In the spirit of TDD I follow this up with the shell of my Presenter class

public class Presenter
{
    public Presenter(IView view)
    {
        throw new NotImplementedException("Not implemented.");
    }
}

And this is where I reached my first dilemma. What is the best way to write my first unit test to test that the presenter class attaches itself to the view’s events? Well I could write a stub class that implements the interface and add a method to the stub that will raise the event. In this example, that would be quite easy, but in the real world, the interface might have multiple properties or methods and why bother going through the trouble to implement them all just to test one event? This is where a mock testing framework such as Rhino Mocks comes into play.

[Test]
public void VerifyAttachesToViewEvents()
{
    MockRepository mocks = new MockRepository();
    IView viewMock = (IView)mocks.CreateMock(typeof(IView));
    viewMock.Load += null;
    LastCall.IgnoreArguments();
    mocks.ReplayAll();
    new Presenter(viewMock);
    mocks.VerifyAll();   
}

The second line of code creates a dynamic proxy that implements the IView interface. In the third line, I set an expectation that the Load event will be attached to. The line afterwards tells Rhino Mocks to ignore the arguments in the last call. In other words, the Rhino Mocks will expect the that the Load event will be attached, but don’t worry about which method delegate gets attached to the event. Without that line, the test would expect that null is attached to the load event, which we do not want.

Finally we call ReplayAll(). I kinda think this is a misnomer since what it really is doing, as far as I know, is telling the mock framework that we are done setting all our expectations and we are going to actually conduct the test now. Up until this method call, every method, property, or event set on the mock instance is telling the mock that we are going to call that particular member. If one of the expected members is not invoked, the test has failed.

So finally after setting all these expectations, I create an instance of Presenter which is the code being tested. I then ask the mock framework to verify that all our expectations were met. Of course this test fails, which is good, since I haven’t yet implemented the presenter. Implementing the presenter is pretty straightforward.

public class Presenter
{
    IView view;
    public Presenter(IView view)
    {
        this.view = view;
        this.view.Load += new EventHandler(view_Load);
    }

    void view_Load(object sender, EventArgs e)
    {
        throw new Exception("Not implemented.");
    }
}

Now my test passes. But wait! It gets better. Now suppose I want to write a new test to test that the presenter handles the Load event. How do I raise the Load event on my mock IView instance? Rhino Mocks provides a way. First I will add a boolean property to the Presenter class named EventLoaded and then write the following test. This will allow me to know whether or not the event was raised. This is a contrived example of course. In a real project, you probably have some other condition you could test to verify that an event was raised.

I then write my test.

[Test]
public void VerifyLoadEventHandled()
{
    MockRepository mocks = new MockRepository();
    IView viewMock = (IView)mocks.CreateMock(typeof(IView));
    viewMock.Load += null;
    IEventRaiser loadRaiser 
         = LastCall.IgnoreArguments().GetEventRaiser();
    mocks.ReplayAll();
    Presenter presenter = new Presenter(viewMock);
    loadRaiser.Raise(viewMock, EventArgs.Empty);
    mocks.VerifyAll();
    Assert.IsTrue(presenter.EventLoaded);
}

This test looks similar to the last test, but note the bolded line (fourth line). This line creates an event raiser for the Load event (ignoring arguments to the event of course). Now I can use that later to raise the event after I create the presenter. Running this test fails, as expected. We have to finish the implementation of the Presenter class as follows:

public class Presenter
{
    IView view;
    public Presenter(IView view)
    {
        this.view = view;
        this.view.Load += new EventHandler(view_Load);
    }

    public bool EventLoaded
    {
        get { return this.eventLoaded; }
        set { this.eventLoaded = value; }
    }

    bool eventLoaded;

    void view_Load(object sender, EventArgs e)
    {
        this.eventLoaded = true;
    }
}

Now when I run the test, it succeeds. Pretty dang nifty. Many thanks to Ayende for clearing up some confusion I had with the Rhino Mocks documentation surrounding events.

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

24 responses

  1. Avatar for Ashwin
    Ashwin June 25th, 2006

    I saw an old post from you where you mentioned about your Yahoo IM account being attacked by the Phishing technique that uses Geocities.
    Could you please share how you regained control of your yahoo Account. I am a victim of this Phishing and have not been able to regain control..changed the password once but to no avail. Any help will be deeply appreciated..

  2. Avatar for Haacked
    Haacked June 25th, 2006

    I contacted Yahoo Customer Care and they helped me out. http://add.yahoo.com/fast/h...
    Good luck!

  3. Avatar for Chris Hedgate
    Chris Hedgate July 5th, 2006

    Nice writeup, wish I had this when we implemented the same kind of tests for verifying that our presenters attaches to the view's events. But the docs and Rhino Mocks mailing list provided enough for us to get it right (more or less exactly the way you describe here).
    However, recently we have changed our code a bit. Instead of having our views raising events that the presenters react to, we just have our views call the "event handler" methods in the presenter directly. This simplified writing the tests (as well as the views of course) a lot, and since there is always a one-to-one relationship between a view and a presenter we saw no need to use events. If there where many presenters that should be notified then events are go, but we thought this change was correct here and simplified things.
    Any comments on that? More or less every example of MVP (in .Net) that I have seen uses events, so maybe we are missing something.

  4. Avatar for Haacked
    Haacked July 5th, 2006

    Chris, that's interesting since most examples I've seen don't use events. The reason I started examining the event approach is that it decouples the view and presenter just a bit more. For example, the view doesn't need to know which methods of the presenter to call, it just raises its events as it normally does.
    Most examples I've seen take the approach that you take and I think it may well end up being more readable. I'm still experimenting with different approaches.

  5. Avatar for Chris Hedgate
    Chris Hedgate July 6th, 2006

    I think it's because Fowler uses events in the C# example from the MVP page of eaaDev. It's true that it decouples the view from the presenter, but I am not sure it is worth it vs readability and ease of writing tests. We are experimenting as well so it is interesting to see where you end up.

  6. Avatar for Tyler Gannon
    Tyler Gannon October 2nd, 2006

    I haven't tried what Chris suggests, for doing away with the events in this model, but IMO the events deliver the power of the model.
    The thing is, there really shouldn't be a one-to-one relationship between model and presenter, if you're writing unit tests. Finally, we started doing this in the first place because it's REALLY hard to unit test your view, so with each bit the view has to know about the model you take, IMO, one step backwards.
    Thanks for the article Phil, always a sound read.

  7. Avatar for EZWeb guy: Jeffrey Palermo [C#
    EZWeb guy: Jeffrey Palermo [C# October 25th, 2006

    I think mock objects themselves are a 300-level topic. I wish it weren't so, but from the folks I talk

  8. Avatar for you've been HAACKED
    you've been HAACKED December 13th, 2006

    [Tip Jar] Unit Test Events With Anonymous Delegates

  9. Avatar for you've been HAACKED
    you've been HAACKED March 27th, 2007

    Rhino Mocks 3.0 Released

  10. Avatar for Community Blogs
    Community Blogs March 27th, 2007

    Ayende just announced the release of Rhino Mocks 3.0 . The downloads are located here . If you aren’t

  11. Avatar for Judah
    Judah April 18th, 2007

    Hey thanks alot -- this really helped me pick up events. I was afraid I'd have to use reflection to get the event handler name...but this gets around that. Brilliant! Thanks a bunch.

  12. Avatar for Al
    Al April 24th, 2007

    Hi Phil, thanks for the post. I also started using RM for mock tests. I hit a snag though. Running the test throws this "Error1TestCase 'AS.Com.Business.ProjectSpace.Tests.ProjectAddViewPresenterTests.AddNewProject'
    failed: System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation.
    ----> System.NullReferenceException : Object reference not set to an instance of an object.
    This is my view:
    ---------------------------
    public interface IProjectAddView
    {
    Project Project { get; }
    event EventHandler ProjectAdded;
    }
    This is my presenter:
    -----------------------------
    public class ProjectAddViewPresenter
    {
    private IProjectAddView _view;
    public ProjectAddViewPresenter(IProjectAddView view)
    {
    if (null == view)
    {
    throw new PresenterException("IProjectView can not be null.");
    }
    _view = view;
    SubscribeToViewEvents();
    }

    public void SubscribeToViewEvents()
    {
    _view.ProjectAdded += new EventHandler(OnProjectCreated);
    }
    void OnProjectCreated(object sender, EventArgs args)
    {
    ProjectManager.CreateProject(_view.Project);
    }
    }

    This is my test:
    -----------------------------
    [Test]
    public void AddNewProject()
    {
    MockRepository mocks = new MockRepository();
    IProjectAddView viewMock = (IProjectAddView)mocks.CreateMock(typeof(IProjectAddView));
    viewMock.ProjectAdded += null;
    IEventRaiser raiser = LastCall.IgnoreArguments().GetEventRaiser();
    mocks.ReplayAll();
    ProjectAddViewPresenter presenter = new ProjectAddViewPresenter(viewMock);
    raiser.Raise(viewMock, EventArgs.Empty);
    mocks.VerifyAll();
    }
    Do you have any idea why is the exception? I am sorry for such long post but I can not resolve this. Thanks a bunch in advance.

  13. Avatar for Haacked
    Haacked April 24th, 2007

    My guess is that the NullReferenceException you're seeing is due to this call:
    ProjectManager.CreateProject(_view.Project);
    But it's hard to say without a stack trace. _view.Project is going to be null unless you mock that property. Something like:
    Setup.Call(viewMock.Prop).PropertyBehavior();
    For more on this, check out the Rhino Mocks documentation wiki.

  14. Avatar for Brian Donahue
    Brian Donahue May 29th, 2007

    Hey Phil.
    I've been playing with this, and read yours and Boodhoo's articles. He does not show an example of the concrete view code, and I was wondering if there any way around the awkward event wiring code in the concrete view? It seems confusing and redundant to have to wire up a "dummy" event in the codebehind that then checks to see if the Handler has subscribers, and if so, evokes the Handler.

  15. Avatar for Haacked
    Haacked May 29th, 2007

    @Brian - I agree. I'm actually working on an update which will include some lessons learned. :) I now advocate a middle ground which I will explain.

  16. Avatar for Irfan
    Irfan June 12th, 2007

    I downLoaded the rhino mock 3.1
    the interface IEventRaiser is not there.
    what coould be the problem.
    do i need to use some other DLL for that interface?

  17. Avatar for Julian Birch
    Julian Birch December 2nd, 2007

    I could be wrong, but I imagine your problem is that you need:
    using Rhino.Mocks.Interfaces;

  18. Avatar for Luke
    Luke January 23rd, 2008

    @Brian - I think I'm running into the same problem here. Every single one of my tests that invokes the presenter has to do all the dummy event wiring. This means that if I down the road add a new event handler that will be wired in the presenter than I have to update all of my tests... really annoying. Anyone know a good work around on this?

  19. Avatar for Justin
    Justin February 19th, 2008

    Thanks again Phil,
    This is just another one of my stops on the road to implementing MVP - Supervising Controller with Rhino Mocks to test them.
    Saved me again :D

  20. Avatar for Chris Woodward
    Chris Woodward May 21st, 2008

    One alternative to Chris Hedgate having his Views call the presenter directly and so needing to know about it, is to have an intermediary class than listens to the View and calls the appropriate method on the Presenter.
    This means your unit tests that test the presenter logic don't need to deal with events. Testing that the intermediary maps View events to the correct Presenter method calls would be easier to test.

  21. Avatar for David White
    David White August 2nd, 2008

    Ayende appears to have deprecated the RhinoMocks approach your example shows. Do you know of the equivalent commands to use in RhinoMocks 3.5?

  22. Avatar for Sanket
    Sanket September 24th, 2008

    it was awesome, thanks; And thanks to Ayende

  23. Avatar for shahulmydeenali
    shahulmydeenali July 21st, 2012

    Nice example.Super

  24. Avatar for Rakesh Kondvilkar
    Rakesh Kondvilkar July 30th, 2014

    Super :-) Thnx