How to Use Rhino Mocks/Printable version
This is the print version of How to Use Rhino Mocks You won't see this message or any elements not part of the book's content when you print or preview this page. |
The current, editable version of this book is available in Wikibooks, the open-content textbooks collection, at
https://en.wikibooks.org/wiki/How_to_Use_Rhino_Mocks
Introduction
Basic Usage
edit1. Create a mock repository:
MockRepository mocks = new MockRepository();
2. Add a mock object to the repository:
ISomeInterface robot = (ISomeInterface)mocks.CreateMock(typeof(ISomeInterface));
If you're using C# 2.0, you may use the generic version and avoid upcasting:
ISomeInterface robot = mocks.CreateMock<ISomeInterface>();
3. "Record" the methods that you expect to be called on the mock object:
// this method has a return type, so wrap it with Expect.Call
Expect.Call(robot.SendCommand("Wake Up")).Return("Groan");
// this method has void return type, so simply call it
robot.Poke();
Note that the parameter values provided in these calls represent those values we expect our mock to be called with. Similarly, the return value represents the value that the mock will return when this method is called.
You may expect a method to be called multiple times:
// again, methods that return values use Expect.Call
Expect.Call(robot.SendCommand("Wake Up")).Return("Groan").Repeat.Twice();
// when no return type, any extra information about the method call
// is provided immediately after via static methods on LastCall
robot.Poke();
LastCall.On(robot).Repeat.Twice();
4. Set the mock object to a "Replay" state where, as called, it will replay the operations just recorded.
mocks.ReplayAll();
5. Invoke code that uses the mock object.
theButler.GetRobotReady();
6. Check that all calls were made to the mock object.
mocks.VerifyAll();
Out and Ref Parameters
Both out and ref parameters are supported.
At the end of the Expect statement, add .OutRef(outOrRefParam0, outOrRefParam1,...) as shown below.
int theRef = 42;
int theOut = 0;
Expect.Call(obj.MyMethod(ref theRef, 0, out theOut)).Return(True).OutRef(13, 666);
Or, if you are using lambda-style expectations:
obj.Expect(x => x.MyMethod(ref theRef, 0, out theOut)).Return(True).OutRef(13, 666);
The variables/objects in OutRef() have the same order as for the method call, ignoring any parameters that are not out or ref.
Testing Events
Rhino Mocks can be used to test events on interfaces. For example, let's assume an interface that represents an editor view. This might in turn be implemented by an untested, lightweight user control in a WinForms application:
public interface IView
{
event EventHandler UserClickedSomething;
}
We'll be testing the presenter class of this view. An NUnit test that asserts some action takes place due to this event being raised might look like this:
[Test]
public void UserClickedSomething_Handled()
{
MockRepository mocks = new MockRepository();
// create the mock
IView viewMock = (IView)mocks.CreateMock(typeof(IView));
// indicate that we expect an event handler to be attached.
// in doing so, we obtain an IEventRaiser instance.
viewMock.UserClickedSomething += null;
IEventRaiser raiser = LastCall.IgnoreArguments().GetEventRaiser();
// we've finished recording.
mocks.ReplayAll();
// create our presenter
Presenter presenter = new Presenter(viewMock);
// no indication from the user yet.
Assert.IsFalse(presenter.IsUserAlive);
// simulate user action by raising event on mocked interface.
raiser.Raise(viewMock, EventArgs.Empty);
mocks.VerifyAll();
// user is known to be alive and kicking.
Assert.IsTrue(presenter.IsUserAlive);
}
Debugging Exceptions
ExpectationViolationException
editThe first time I got an ExpectationViolationException , I was baffled. Here's what it looked like:
failed: Rhino.Mocks.Exceptions.ExpectationViolationException : IInternalSearchService.ProcessIndexingRequest(collaboration.thecompany.com.internalServiceContractsObjects._2007._12.AuthenticationCredentials, "Zardoz", \[collaboration.thecompany.com.internalSearch._2007._12.IndexingWork\]); Expected #0, Actual #1. IInternalSearchService.ProcessIndexingRequest(collaboration.thecompany.com.internalServiceContractsObjects._2007._12.AuthenticationCredentials, "Zardoz", \[collaboration.thecompany.com.internalSearch._2007._12.IndexingWork\]); Expected #1, Actual #0. |
Cryptic, and ultimately not very helpful (at least to me). Basically, all you know when seeing this is that something went wrong with an expectation you set up in your test. Here are a few more things to know, and things you can do to try and debug these kinds of problems:
- Expectations have to do with both the expected calls _and_ the expected parameters. If, in your test, you say something like, Expect.Call(myMock.DoSomething(fooObject)), Rhino.Mocks expects that myMock.DoSomething will be called with _the exact instance_ of fooObject.
- When Rhino.Mocks compares expected parameters (like fooObject in the previous point), it uses the object's Equals() method. This means that even if your expected object has all the same property values, etc., comparison will fail unless you are able to pass the _exact_ expected instance (or unless you override Equals(), which is generally a not-so-great idea). In 3.4, Rhino.Mocks introduced the Property.AllPropertiesMatch() constraint which does a reflective property comparison for objects, e.g.,
Expect.Call(myMock.DoSomething(fooObject)).IgnoreArguments().Constraints(Property.AllPropertiesMatch(fooObject)); |
- I.e., expect a call to myMock.DoSomething, "ignoring" all passed arguments, but constrained by the following: that the first arguments properties match fooObject's properites exactly.
- You can add a Rhino.Mocks logger to print data to the console. This can give a bit more detail on what's happening where and why. Just add the following line to your test, before the mocking work:
RhinoMocks.Logger = new TextWriterExpectationLogger(Console.Out); |
- When Rhino.Mocks compares two objects during expectation validation, and something goes wrong, the data you get in the exception message comes from the ToString() on the objects. So, you can override ToString() in the class of the objects which are being compared; override it to give you some info about what might be going wrong with the comparison (like, override it to spit out the various properties on the objects).
Mocks, Dynamic Mocks, Partial Mocks
The difference between these three types of Rhino Mocks mocking solutions has always been a little confusing to me. So, here's my best shot at some concise definitions:
- A normal mock object will throw an exception when a method, which has no explicit expectation defined, is called on the mock instance.
- A dynamic mock, instead of throwing an exception, will return null or 0 for any unexpected method calls.
- A partial mock, like a dynamic mock, will not throw an exception if a method is called when there is no expectation defined for the method; but, instead of returning 0 or null, a partial mock will call the actual implementation method (i.e., not the mock) on the object and return that value. This lets you selectively mock specific methods on an object.