Requirements
We’re going to work through a simple set of scenarios/tests using RhinoMocks and NUnit. So we’ll need the following to get started
I’m using the RhinoMocks NuGet package, version 3.6.1 for these samples and the NuGet package for NUnit 2.6.3.
What are we going to mock
For the sake of these examples we’re going to have an interface, which we will mock out, named ISession. Here’s the code for ISession
public interface ISession
{
/// <summary>
/// Subscribe to session events
/// </summary>
/// <param name="o">the object that recieves event notification</param>
/// <returns>a token representing the subscription object</returns>
object Subscribe(object o);
/// <summary>
/// Unsubscribe from session events
/// </summary>
/// <param name="token">a token supplied via the Subscribe method</param>
void Unsubscribe(object token);
/// <summary>
/// Executes a command against the session object
/// </summary>
/// <param name="command">the command to be executed</param>
/// <returns>an object representing any return value for the given command</returns>
object Execute(string command);
}
Getting started
Before we can create any mock objects we need to create an instance of the factory class or more specifically the MockRepository. A common pattern within NUnit test fixtures is to create this during the SetUp, for example
[TestFixture]
public class DemoTests
{
private MockRepository repository;
[SetUp]
public void SetUp()
{
repository = new MockRepository();
}
}
The MockRepository is used to create mock objects but also can be used to record, replay and verify mock objects (and more). We’ll take a look at some of these methods as we go through this step by step guide.
If, as is often the cases with tests I’ve written with RhinoMock, we want to verify all expectations on our mocks. Using NUnit we can handle this in the TearDown, for example adding the following to the DemoTests class
[TearDown]
public void TearDown()
{
repository.VerifyAll();
}
this will verify that all mock expectations have been met when the test fixture is torn down.
Mocks vs DynamicMocks
In previous posts on Moq and JustMock Lite I’ve mentioned strict and loose behavior. Basically loose behavior on a mock object means I do not need to supply expectations for every method call, property invocation etc. on a mocked object, whereas strict means the opposite, in that if we do not supply the expectation an ExpectationViolationExpectation will be thrown by RhinoMocks.
In RhinoMock terminology, mocks have strict semantics whereas dynamic mocks have loose.
So, to see this in action let’s add two tests to our test fixture
[Test]
public void TestMock()
{
ISession session = repository.CreateMock<ISession>();
repository.ReplayAll();
Mapper mapper = new Mapper(session);
}
[Test]
public void TestDynamic()
{
ISession session = repository.DynamicMock<ISession>();
repository.ReplayAll();
Mapper mapper = new Mapper(session);
}
Our Mapper class looks like the following
public class Mapper
{
private readonly ISession session;
private object token;
public Mapper(ISession session)
{
this.session = session;
token = session.Subscribe(this);
}
}
don’t worry about repository.ReplayAll(), we’ll get to that in a minute or two.
Now if we run these two tests TestDynamic will succeed whereas TestMock will fail with an ExpectationViolationException. The dynamic mock worked because of the loose semantics which means it does not require all expectations set before usage. We can fix the TestMock method by writing an expectation for the call to the Subscribe method on the ISession interface.
So changing the test to look like the following
[Test]
public void TestMock()
{
ISession session = repository.CreateMock<ISession>();
Expect.Call(session.Subscribe(null)).IgnoreArguments().Return(null).Repeat.Any();
repository.ReplayAll();
Mapper mapper = new Mapper(session);
}
So in the above code we arrange our expectations. Basically we’re saying expect a call on the Subscribe method of the session mock object. In this case we pass in null and tell the mock to ignore the arguments, removing IgnoreArguments means we expect that Mapper will call the Subscribe method passing the exact arguments supplied in the expectation, i.e. in this case null.
Next we’re setting the expectation to return null and we don’t care how many times this method is called, so we call Repeat.Any(). If we wish to change the expectation to ensure the method is called just the once, we can change this to Repeat.Once() which is obviously more specific and useful for catching scenarios where a method is accidentally called more times than necessary. In our Mapper’s case this cannot happen and the method can only be called once, so we’d normally set this to Repeat.Once().
What we’ve done is supply defaults which is what the dynamic mock object would have probably implemented for our expectations as well. Hence why I used Repeat.Any() to begin with, so the implementation above will now cause the test to succeed.
Record/Playback
Now to return to repository.ReplayAll(). RhinoMocks works in a record/playback way, that is by default it’s in record mode so if in TestDynamic we comment out repository.ReplayAll() we’ll get the exception InvalidOperationException. The mock object is in a record state. We arrange our expectation in the record phase then act upon them during playback. As we are, by default, in record mode we can simply start creating our expectations, then when we’re ready to act on those mocked objects we switch the MockRepository to playback mode using repository.ReplayAll().
Arrange
As already mentioned we need to setup expectations on our mock object (unless we’re using dynamic mocks ofcourse). We do this during the arrange phase as was shown with the line
Expect.Call(session.Subscribe(null)).IgnoreArguments().Return(null).Repeat.Any();
One gotcha is if your method takes no arguments and returns void. So let’s assume ISession now has a method DoSomething which takes no arguments and returns void and see what happens…
Trying to write the following
Expect.Call(session.DoSomething()).Repeat.Any();
will fail to compile as we cannot convert from void to Rhino.Mocks.Expect.Action, we can easily fix by removing the () and using the following line
Expect.Call(session.DoSomething).Repeat.Any();
Equally if the ISession had a property named Result which was of type string we can declare the expectation as follows
Expect.Call(session.Result).Return("hello").Repeat.Any();
We can also setup an expectation on a method call using the following
session.Subscribe(null);
LastCall.IgnoreArguments().Return(null).Repeat.Any();
in this case the LastCall allows us to set our expectations on the previous method call, i.e. this is equivalent to our previous declaration for the expectation on the Subscribe method. This syntax is often used when dealing with event handlers.
Mocking Event Handlers
Let’s assume we have the following on the ISession
event EventHandler StatusChanged;
the idea being a session object may change, maybe to a disconnect state, and we want to have the Mapper respond to this in some way. Then we want to cause events to fire and then see whether the Mapper changes accordingly.
Okay, so let’s rewrite the Mapper constructor to look like the following
public Mapper(ISession session)
{
Status = "Connected";
this.session = session;
session.StatusChanged += (sender, e) =>
{
Status = "Disconnected";
};
}
The assumption is that we have a string property Status and that if a status change event is received the status should switch from Connected to Disconnected.
Firstly we need to handle the expectation of the += being called on the event in ISession, so our test would look like this
[Test]
public void TestMock()
{
ISession session = repository.CreateMock<ISession>();
session.StatusChanged += null;
LastCall.IgnoreArguments();
repository.ReplayAll();
Mapper mapper = new Mapper(session);
Assert.AreEqual("Connected", mapper.Status);
}
Notice we use LastCall to create an expectation on the += being called on the StatusChanged event. This should run without any errors.
Now we want to change things to see if the Mapper Status changes when a StatusChanged event takes place. So we need a way to raise the StatusChanged event. RhinoMocks includes the IEventRaiser interface for this, so rewriting our test as follows, will solve this requirement
[Test]
public void TestMock()
{
ISession session = repository.CreateMock<ISession>();
session.StatusChanged += null;
LastCall.IgnoreArguments();
IEventRaiser raiser = LastCall.GetEventRaiser();
repository.ReplayAll();
Mapper mapper = new Mapper(session);
Assert.AreEqual("Connected", mapper.Status);
raiser.Raise(null, null);
Assert.AreEqual("Disconnected", mapper.Status);
}
Notice we use the LastCall.GetEventRaiser() to get an IEventRaiser. This will allow us to raise events on the StatusChanged event. We could simply combine the LastCall’s to form
IEventRaiser raiser = LastCall.IgnoreArguments().GetEventRaiser();
The call raiser.Raise(null, null) is used to actually raise the event from our test, the two arguments match the arguments on an EventHandler, i.e. an object (for the sender) and EventArgs.
More types of mocks
Along with CreateMock and DynamicMock you may notice some other mock creation methods.
What are the *MultiMocks?
CreateMultiMock and DynamicMultiMock allow us to create a mock (strict semantics for CreateMultiMock and loose for DynamicMultiMock) but supporting multiple types. In other words let’s assume our implementation of ISession is expected to support another interface, IStatusUpdate and this will have the event we’re previously declare, i.e.
public interface IStatusUpdate
{
event EventHandler StatusChanged;
}
Now we change the Mapper constructor to allow it to check if the ISession also supports IStatusUpdate and only then subscribe to it’s event, for example
public Mapper(ISession session)
{
Status = "Connected";
this.session = session;
IStatusUpdate status = session as IStatusUpdate;
if (status != null)
{
status.StatusChanged += (sender, e) =>
{
Status = "Disconnected";
};
}
}
and finally let’s change the test to look like
[Test]
public void TestMock()
{
ISession session = repository.CreateMultiMock<ISession>(typeof(IStatusUpdate));
((IStatusUpdate)session).StatusChanged += null;
IEventRaiser raiser = LastCall.IgnoreArguments().GetEventRaiser();
repository.ReplayAll();
Mapper mapper = new Mapper(session);
Assert.AreEqual("Connected", mapper.Status);
raiser.Raise(null, null);
Assert.AreEqual("Disconnected", mapper.Status);
}
As you can see, we’ve now created an mock ISession object which also supports IStatusUpdate.
PartialMock
The partial mock allows us to mock part of a class. For example, let’s do away with our Mapper and just write a test to check what’s returned from this new Session class
public class Session
{
public virtual string Connect()
{
return "none";
}
}
and our test looks like this
[Test]
public void TestMock()
{
Session session = repository.PartialMock<Session>();
repository.ReplayAll();
Assert.AreEqual("none", session.Connect());
}
This will run and succeed when we use the PartialMock as it automatically uses the Session objects Connect method, but we can override this by using the following
[Test]
public void TestMock()
{
Session session = repository.PartialMock<Session>();
Expect.Call(session.Connect()).Return("hello").Repeat.Once();
repository.ReplayAll();
Assert.AreEqual("hello", session.Connect());
}
Now if instead we use CreateMock in the above this will still work, but if we remove the Expect.Call the mock does not fall back to using the Session Connect method but instead fails with an exception, ExpectationViolationException.
So if you need to mock a concrete object but have the code use the concrete class methods in places, you can use the PartialMock.
Note: You methods on the Session class need to be marked as virtual for the above to work
Obviously a PartialMultiMock can be used to implement more than one type.
Stubs
A stub is generally seen as an implementation of a class with minimal functionality, i.e. if we were to implement any of our ISession interfaces (shown in this post) and for properties we simply set and get from a backing store, methods return defaults and do nothing. Methods could return values but it’s all about minimal implementations and consistency. Unlike mocks we’re not trying to test behavior, so we’re not interested in whether a method was called once or a hundred times.
Often a mock with loose semantics will suffice but RhinoMock includes a specific type that’s created via
repository.Stub<ISession>();
the big difference between this and a dynamic mock is that in essense properties are all declared as
Expect.Call(session.Name).PropertyBehavior();
implicitly (PropertyBehavior is discussed in the next section). This means if we run a test using a dynamic mock, such as
ISession session = repository.DynamicMock<ISession>();
repository.ReplayAll();
session.Name = "Hello";
Assert.AreEqual(null, session.Name);
The property session.Name will be null even though we assigned it “Hello”. Using a stub, RhinoMocks gives us an implementation of the property setter/getter and thus the following would result in a test passing
ISession session = repository.DynamicMock<ISession>();
repository.ReplayAll();
session.Name = "Hello";
Assert.AreEqual("Hello", session.Name);
i.e. session.Name now has the value “Hello”.
Mocking properties
So, we’ve got the following interface
public interface ISession
{
string Name { get; set; }
}
now what if we want to handle the getter and setter as if they were just simple setters and getters (i.e. implemented exactly as shown in the interface). Instead of creating return values etc. we can use a short cut
Expect.Call(session.Name).PropertyBehavior();
which basically creates an implementation of the property which we can now set and get from without setting full expectations, i.e. the following test shows us changing the Name property after the replay
[Test]
public void TestMock()
{
ISession session = repository.CreateMock<ISession>();
Expect.Call(session.Name).PropertyBehavior();
repository.ReplayAll();
session.Name = "Hello";
Assert.AreEqual("Hello", session.Name);
}