Using Rhino Mocks To Unit Test Events on Interfaces
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.
Comments
24 responses