views:

686

answers:

1

Being new to RhinoMocks and Unit Testing, I have come accross an issue that I cannot seem to find a resolution to (no matter how much documentation I read).

The issue is this: I have created an Interface that exposes 5 Events (to be used for a view in ASP.NET and the MVP Supervisory Controller pattern..... I know, I should be using MVC, but that's a whole other issue). Anyway, I want to test that when a certain event fires on the view, we'll call it "IsLoaded", that a method inside of my Presenter is called and, using Dependency Injection, a value is returned from the Dependency and set to the view. Here is where the problem starts: when I use Expect.Call(Dependency.GetInfo()).Return(SomeList), the Call never executes (without the mock.ReplayAll() method being invoked). Well, when I invoke the ReplayAll method, I get ExpectationExceptions because of the Subscription by the Presenter object to the other Events exposed by the View Interface.

So, for me to test that IView.IsLoaded has fired, I want to verify that IView.ListOfSomething has been updated to match the list I passed in via the Expect.Call(). However, when I set the expectation, the other Event subscriptions (which occur straight out of the constructor for the Presenter) fail the #0 Expectations of the test. What I get is, view.Save += this.SaveNewList tosses up a RhinoMocks ExpectationViolationException.

My million dollar question is this: Is it necessary I set expectations for ALL of my events (via [Setup]), or is there something that I'm missing/not understanding about how Unit Testing or RhinoMocks works?

Please bear in mind I am extremely new to Unit Testing, and therefore RhinoMocks. If it appears I don't know what I'm talking about, please feel free to point that out.

+1  A: 

I'm working on a project where we used MVP and rhino mocks as well. What we did was simply expect all event subscriptions in every test.

    private void SetupDefaultExpectations()
    {
        _mockView.Initializing += null; LastCall.IgnoreArguments();
        _mockView.SavingChanges += null; LastCall.IgnoreArguments();
    }

Then we built a extension method on IMockedObject (from RhinoMocks) to trigger events in the unit tests and un-wrap exceptions so that they can be expected in the standard NUnit way.

static class IMockedObjectExtension
{
    public static void RaiseEvent(this IMockedObject mockView, string eventName, EventArgs args)
    {
        EventRaiser eventraiser = new EventRaiser(mockView, eventName);

        try
        {
            eventraiser.Raise(mockView, args);
        }
        catch (TargetInvocationException ex)
        {
            throw ex.InnerException;
        }
    }

    public static void RaiseEvent(this IMockedObject mockView, string eventName)
    {
        RaiseEvent(mockView, eventName, EventArgs.Empty);
    }
}

This could then be used from the unit test like this

using(_mocks.Record())
{
    Expect.Call(dependency.GetInfo()).Return(someList);
}
using(_mocks.Playback())
{
    Presenter presenter = new Presenter(_mockView, dependency);
    (_mockView as IMockedObject).RaiseEvent("SavingChanges");
}

To eliminate duplication between presenter tests we have refactored this to a BasePresenterTest base class which sets up this basic structure for all presenter tests and exposes helper methods to the sub class.

public abstract class BasePresenterTest<VIEW> where VIEW : IBaseView
{
    protected MockRepository _mocks;
    protected VIEW View { get; private set; }

    protected abstract void SetUp();
    protected abstract void TearDown();
    protected abstract void SetupDefaultExpectations();

    [SetUp]
    public virtual void BaseSetUp()
    {
        _mocks = new MockRepository();
        View = _mocks.CreateMock<VIEW>();

        SetUp();
    }

    [TearDown]
    public virtual void BaseTearDown()
    {
        TearDown();

        View = null;
        _mocks = null;
    }

    protected virtual void BaseSetupDefaultExpectations()
    {
        //Setup default expectations that are general for all views
        SetupDefaultExpectations();
    }

    protected virtual IDisposable Record()
    {
        IDisposable mocksRecordState = _mocks.Record();

        BaseSetupDefaultExpectations();

        return mocksRecordState;
    }

    protected virtual IDisposable Playback()
    {
        return _mocks.Playback();
    }

    protected void RaiseEventOnView(string eventName)
    {
        (View as IMockedObject).RaiseEvent(eventName);
    }       
}

This eliminates alot of code from the tests in our project.

We still use a old version of RhinoMocks but I will try to update this once we move to a later version.

maz