Rhino Mocks Version 2.0
A dynamic mock object framework for the .Net platform. It's purpose is to
ease testing by allowing the developer to create mock implementations of custom
objects and verify the interactions using unit testing.
Rhino.Mocks is an attempt to create easier way to build and use mock objects
and allow better refactoring support from the current tools. It's a hybrid
approach between the pure Record/Replay of
EasyMock.Net's model
and NMock's expectation based model.
Rhino.Mocks originated from
EasyMock.Net and
attempt to improve on their model to create easy to use and power mocking
framework. It's free for use and modification for free and commercial software.
I created Rhino.Mocks because I wanted better support from refactoring tools
such as ReSharper and because I don't like the way
NMock handle parameterized methods (you need
to pass fake constraints to get it to recognize the correct method) and the lack
of extensibility [I required hacks to get to the result that I wanted].
Licensing: Rhino Mocks is Free
Software which is released under the BSD license.
You can get Rhino Mocks at its project page
What does Rhino Mocks offers?
- Explicit record & replay model for expectations.
- Working with strongly typed mocks.
- Expectations based on:
- Arguments matching
- Constraints matching
- Custom callback to verify the expected arguments using your own code
- Setting actions on methods, return spesific value, or throw an
exception.
Why reinvent the wheel?
- Other frameworks, such as NMock,
NMock2 or
TypeMock.Net uses string baed
approach for mocking call. This means that you cannot use the compiler and
most refactoring tools in order to catch any mistakes until runtime, if
then. After getting burned more than once by this, I decided that I want a
mocking framework that would use the objects, and not strings, in order to
set up expectations on the mocked object.
- Why not use
EasyMock.Net, then?
- EasyMock.Net
is a port of a Java library, and it uses Remoting proxies to do its
work.
- I didn't like the API that
EasyMock.Net
had, it had too much roots in Java to be comfortable to work with in
.Net
- I found several places where a the Remoting proxies were behaving
exactly like the real objects. Specifically, they coulnd't be copied
using Array.Copy();
- I Never had much interest in Remoting, so I don't know a lot about
it
I hope you will enjoy using Rhino Mocks.
In the first
article, I had explained a little about mock objects,
interaction base testing, state based test, etc. Here is a brief refreshment for
those who don't want to read the full
article.
- Mock Object - an object that pretend to be another object, and allows to
set expectations on its interactions with another object.
- Interaction Based Testing - you specify certain sequence of interactions
between objects, initiate an action, and then verify that the sequence of
interactions happened as you specified it.
- State Based Testing - you initiate an action, and then check for the
expected results (return value, property, created object, etc).
- Expectation - general name for validation that a particular method call
is the expected one.
- Record & Replay model - a model that allows for recording actions on a
mock object, and then replaying and verifying them. All mocking frameworks
uses this model. Some (NMock, TypeMock.Net, NMock2) uses it implicitly and
some (EasyMock.Net, Rhino Mocks) uses it explicitly.
- Ordering - The ability to specify that replaying a sequence of method
calls will occur in a specific order (or disorder).
So, what has changed?
In version 2.0, I moved from the
EasyMock.Net based code, which relied on Remoting's proxies; to inheritance
approach using Castle.DynamicProxy. The resulting code base is much more
flexible and easier to extend. While rebuilding it, I paid attention to several
factors.
Rhino Mocks capabilities:
Let's get down to the code, okay? [I tried to create an example for
this article, but the example was both contrived and didn't portrayed the
possibilities correctly. So most of the code samples here were taken from
NHibernate Query Analyzer tests and are "real code".]
The purpose of mock objects is to allow you to test the
interactions between components, this is very useful when you test code that
doesn't lend itself easily to state base testing. Most of the examples here are
tests to check that the save routine for a view is working as expected. The
requirements for this method are:
-
If the project is a new project, then ask for a name for the
project. [ Allow canceling save at this point ]
-
If the new project name already exists, ask if user want to
overwrite it. [ If no, cancel the save operation ]
-
If the user approve overwrite, delete old project.
- Save project.
So we have five tests here:
- Project is new project, and the user cancels on new name.
- Project is new project, the user gives a new name, and the project is
saved.
- Project is new project, the user gives a name that already exists and
don't approve overwrite.
- Project is new project, the user gives a name that already exist and
approve overwrite.
- Project already exists, so just save it.
Trying to test that using state based testing is going to be both awkward and
painful, using interaction based testing, it would be a breeze (other types of
tests would be just as painful to test using interaction testing but easy using
state testing.)
That is enough talking, let's see some code:
[Test]
public void SaveProjectAs_CanBeCanceled()
{
using(MockRepository mocks = new MocksRepository())
{
IProjectView projectView = (IProjectView)projectView.CreateMock(typeof(IProjectView));
Project prj = new Project("Example Project");
IProjectPresenter presenter = new ProjectPresenter(prj,projectView);
Expect.On(projectView).Call(projectView.Title).Return(prj.Name);
Expect.On(projectView).Call(projectView.Ask(question,answer)).Return(null);
mocks.ReplayAll();
Assert.IsFalse(presenter.SaveProjectAs());
}
}
We create a MockRepository and create a mock project view, project and a
project presenter, then
we set the expectations. After we finished with setting up the expectations, we
move to the replay state, and call the SaveProjectAs() method, which should
return false if the user canceled the save process.
This
example clarify several key concepts with Rhino Mocks.
- We set expectation on object using the object's methods, and not
strings, this reduce the chances of creating a mistake that will only
(hopefully) be caught at runtime, when you run the tests.
- We are using explicit call to move from Record state to Replay state.
- MockRepository implement IDisposable; so when we reach the end for the
using statement.
This is about as simple example as can be had, the real test move creating
the MockRepository, the project, the view and presenter to the setup method,
since they are require for each test. You can see that we expected two method to
be called, with specific arguments, and that we set a result for each. This
method uses parameter matching expectations, Rhino Mocks supports several
more. More info: Method Calls.
Ordered / Unordered:
Method calls in Rhino Mocks can be ordered or unordered. The default state
for a recorder is unordered recording, this means that during replay, methods
can come at any order. If the recorder is changed to ordered, then the methods
must be called in the exact same order as they were recorded. Here is a code
sample:
[Test]
public void SaveProjectAs_NewNameWithoutConflicts()
{
using(mocks.Ordered())
{
Expect.On(projectView).
Call(projectView.Title).
Return(prj.Name);
Expect.On(projectView).
Call(projectView.Ask(question,answer)).
Return( newProjectName);
Expect.On(repository).
Call(repository.GetProjectByName(newProjectName)).
Return(null);
projectView.Title = newProjectName;
projectView.HasChanges = false;
repository.SaveProject(prj);
}
mocks.ReplayAll();
Assert.IsTrue(presenter.SaveProjectAs());
Assert.AreEqual(newProjectName,prj.Name);
}
In the above code example we ask Rhino Mocks to verify that the calls come in
the exact same order. Notice that we specify expectations on several mock
objects, and Rhino Mocks will handle the ordering between them. This mean that
if I set the project view title before I get a project from the repository, the
test will fail. In Rhino Mocks, the default is to use unordered matching, but it
support unlimited depth of nesting between ordered and unordered, this means
that you can create really powerful expectations. Here is a contrived
example:
[Test]
public void MovingFundsUsingTransactions()
{
using(MockRepository mocks = new MockRepository())
{
IDatabaseManager databaseManager = mocks.CreateMock(typeof(IDatabaseManager));
IBankAccount accountOne = mocks.CreateMock(typeof(IBackAccount)),
accountTwo = mocks.CreateMock(typeof(IBankAccount));
using(mocks.Ordered())
{
Expect.On(databaseManager).
Call(databaseManager.BeginTransaction()).
Return(databaseManager);
using(mocks.Unordered())
{
accountOne.Withdraw(1000);
accountTwo.Deposit(1000);
}
databaseManager.Dispose();
}
mocks.ReplayAll();
Bank bank = new Bank(databaseManager);
bank.TransferFunds(accountOne,accountTwo,1000);
}
}
This code verify that the transfer of funds from one account to another is
wrapped in a transaction, but the implementation is free to withdraw from the
first account first, or to deposit into the second account first, both are
legal, as long as both actions happens. The reverse is true as well, you may
specified unordered sequence of ordered events (I want A then B then C to
happen, and I want D then E then F to happen, but I don't care which sequence
comes first).
Ordering have two caveats:
- To exit an ordering in replay state, you must call all the recorded
methods. In the above example, we can move from the inner Unordered
ordering (the one containing the withdraw and deposit) only after both
methods were called. This fall in line with the way the recording code
looks, so it shouldn't cause any surprises.
- You must exit all ordering before you can start replaying. This is
enforced by the framework (you get an exception if you try).
The IDisposable pattern:
The using(...) syntax & IDisposable are very convenient when it
comes to make sure you're not forgetting to clean up resources, in Rhino Mocks,
it's used in two places:
- MockRepository implement IDisposable so you won't forget to call
VerifyAll(), or call ReplayAll().
- If an exception has been raised during
the test because of Rhino Mocks, the repository won't validate, and you'll
get the exception that was thrown.
-
But if an exception was thrown that didn't originate in Rhino Mocks (from an
Assert that failed, for instance), then the repository will attempt to
verify that all expectations has been met, and will (probably) fail. This
result in an exception that masks the real cause for failure. Keep this in
mind if you have a failing test.
-
The recommended approach is to create the repository in the Setup method,
and verify it in the Teardown method, this way you'll get the correct
exception. Just call VerifyAll() (or Dispose(), which does the same thing)
in your teardown.
- Ordered() and Unordered() methods on MockRepository, these two methods
set the MockRepository to expect the following method in ordered or
unordered way. Those calls should be wrapped in a using declaration (or a
manual Dispose() call). You cannot start replaying with an open ordering
call.
Method Calls:
When Rhino Mocks intercept a call from a mocked object it determines if the
call is expected by checking the method signature, the object this method was
called on, whatever the expectation on this method match the arguments passed
for the method. The matching of the mocked object and the method signature is
not very interesting, but the expectations on a method call is. Rhino Mocks
support the following expectations:
- Matching Parameters - The same arguments that were passed during
record state must be passed during the replay state in order to get a match.
One caveat, though, for arrays, the comparison is done for the objects
contained in the array. This is the default behavior of Rhino Mocks, and
require no action on your part.
- Ignore Parameters - Expect just the method call on this object,
any arguments will be accepted. This is useful if you don't care about the
arguments. To activate this behavior, you need to call IgnoreArguments() on
the IMethodOptions interface (More info: Method
Options Interface).
-
Constraints Matching - Each argument is validated
against a constraint, to accept the call, all constraints must pass. (More
info: Constraints.)
-
Callbacks - A user supplied delegate is called to check
if a method call is valid, if the delegate return true, then the method is
accepted. (More info: Callbacks.)
Callbacks:
A callback is a user supplied delegate that is called whenever Rhino Mocks
needs to evaluate whatever a method call is expected or not. This is useful in
some scenarios when you want to do a complex validation on a method arguments,
or have a complex interactions between objects that you need to mock in the
tests. I added this feature because I want to test some threading code which had
the semantics of: Start Job, and notify me when you're done. The only way to
re-create that without bringing the real thread (and kill tests isolations) was
to plug my own code during the replay state and call the tested object myself.
Some things to consider before you decide to use callbacks:
- It can be abused very easily. It can be very hard to understand tests
that use callbacks, because you get calls from supposedly innocent code. Do
it only if you need it.
- You callback may (and likely will be) called several times; keep that in
mind when you write it. Either wrap it all in a if ( firstTimeCalled ) {
/*do work*/) or make sure that repeated call for the delegate won't
have some nasty side effects.
If it so open to abuse, why add it?
-
Because when you need it, you really need it, and I
would prefer that I'd some nice way to do it, and not a
really
ugly hacks.
- Because I respect those who will use this framework not to take the
power and abuse it.
The technical details - In order to be a valid callback, the callback must
return a Boolean, and have the same arguments as the mocked methods. You
register a delegate using the following code:
IProjectRepository repository = (IProjectRepository) mocks.CreateMock(typeof(IProjectRepository));
IProjectView view = (IProjectView )mocks.CreateMock(typeof(IProjectView ));
Expect.On(view).(view.Ask(null,null)).Callback(new AskDelegate(DemoAskDelegateMethod)).Return(null);
Notice that you cannot change the return value for the method, but must pass
it explicitly.
Recursive Expectations:
One final word of warning regarding callbacks. If your callback will initiate
an action that cause another call on a mocked object, this will fail when
mixed with Ordered(). The reason for that is that the framework cannot decide
whatever to accept the call or not, and calling another mocked method while
evaluating means that the currently expected call is the one still being
evaluated. This will fail the test. Using Unordered(), it will work, since the
second expectation is not dependant on the first one being accepted first. In
short, Ordered() is not re-entrant :-)
Method Options Interface:
The IMethodOptions allows you to set various options on a method call. Here
is an example of telling Rhino Mocks to ignore the arguments of a method:
IProjectRepository repository = (IProjectRepository)mocks.CreateMock(typeof(IProjectRepository));
IProjectView view = (IProjectView ) mocks.CreateMock(typeof(IProjectView ));
Expect.On(view).(view.Ask(null,null)).IgnoreArguments().Return(null);
repository.SaveProject(null);
LastCall.On(repository).IgnoreArguments();
As you can see, we use the Expect.On().Call() for methods that has return
values, and LastCall.On() for methods that return void to get the IMethodOptions
interface. I find the Expect.On().Call() syntax a bit clearer, but there is no
practical difference between the two. I would recommend using Expect wherever
possible (anything that return a value). For properties setters, or methods
returning void, the Expect syntax is not applicable, since there is no return
value. Thus, the need for the LastCall.On(). The idea of Last Call is pervasive
in the record state, you can only set the method options for the last call
- even Expect.On().Call() syntax is merely a wrapper around LastCall.On().
Expect.On().Call() & LastCall.On() allows you to set the following
options:
Expect.On(view).(view.Ask(null,null)).
Return(null);
Expect.On(view).(view.Ask(null,null)).
Throw(new Exception("Demo"));
Expect.On(view).(view.Ask(null,null)).Return(null).
Repeat.Twice();
Expect.On(view).(view.Ask(null,null)).Return(null).
IgnoreArguments();
Expect.On(view).(view.Ask(null,null)).Return(null)
.Constraints(
Text.StartsWith("Some"),
Text.EndsWith("Text"));
Expect.On(view).(view.Ask(null,null)).Return(null).
Callback(new AskDelegate(VerifyAskArguments));
Note: For methods that return a value, you must specify either
a return value or an exception to throw. You will not be able to continue
recording or move to replay state otherwise.
Note II: Method chaining really makes writing this cod
easier.
Constraints:
Constraints are a way to verify that a method arguments match a certain
criteria. Rhino Mocks include a number of built in constraints, and allows you
to define you own custom one, which will integrate cleanly into the framework.
You specify constraints on method arguments using the following syntax:
Expect.On(view).
(view.Ask(null,null)).Return(null).
Constraints(
Text.StartsWith("Some"),
Text.EndsWith("Text"));
You need to pass the exact same number of constraints as the number of
arguments of the method. When a method is called during replay state, Rhino
Mocks evaluate each constraint against the parameter in the same index, and
accept the method if all the constraints were met. As a note, I got the idea of
the current constraints syntax from NMock2, it is much leaner approach to create
constraints.
Rhino Mocks built in constraints:
Rhino mocks ships with the following constraints:
Constraint: |
Example: |
Accepted Value(s): |
Unaccepted Value(s): |
Object |
Anything |
Is.Anything() |
Anything at all {0,"","whatever",null, etc} |
(Nothing at all!) |
Equal |
Is.Equal(3) |
(int)3 |
3f, 4,"", new object() |
Not Equal |
Is.NotEqual(3) or !Is.Equal(3) |
3f,43,null,DateTime.Now |
(int)3 |
Null |
Is.Null() |
null |
5,"",new object() |
Not Null |
Is.NotNull() |
DateTime.Now, "asbcd", 0xHead |
null |
Type Of |
Is.TypeOf(typeof(string)) |
"","Hello",String.Empty |
null, 3, DateTime.Now |
Greater Than |
Is.GreaterThan(10) |
15,100,300 |
3,4,5, 10 |
Greater Than Or Equal |
Is.GreaterThanOrEqual(10) |
10,15,100 |
4,2,8,9 |
Less Than |
Is.LessThan(10) |
1,2,9 |
10,32,100 |
Less Than Or Eqaul |
Is.LessThanOrEqual(10) |
1,9,10 |
11,33,43 |
Property |
Equal To Value |
Property.Value("Length",0) |
new ArrayList(),String.Empty |
"Hello", 5 |
Null |
Property.IsNull("InnerException") |
new Exception("exception without
inner exception") |
new Exception("Exception with inner
Exception", new Exception("Inner") |
Not Null |
Property.IsNotNull("InnerException") |
new Exception("Exception with inner
Exception", new Exception("Inner") |
new Exception("exception without
inner exception") |
List |
Is In List
[the parameter is a collection that contains this value] |
List.IsIn(4) |
new int[]{1,2,3,4}, new int[]{4,5,6} |
new object[]{"",3} |
One Of
[parameter equal to one of the objects in this list] |
List.OneOf(new int[]{3,4,5}) |
3,4,5 |
9,1,"" |
Equal |
List.Equal(new int[]{4,5,6}) |
new int[]{4,5,6}, new object[]{4,5,6} |
new int[]{4,5,6,7} |
Text |
Starts With |
Text.StartsWith("Hello") |
"Hello, World", "Hello, Rhino Mocks" |
"", "Bye, Bye" |
Ends With |
Text.EndsWith("World") |
"World","Champion Of The World" |
"world", "World Seria" |
Contains |
Text.Contains("or") |
"The Horror Movie...", "Either that
or this" |
"Movie Of The Year" |
Like
[perform regular expression validation] |
Text.Like("Rhino|rhino|Rhinoceros|rhinoceros"
) |
"Rhino Mocks", "Red
Rhinoceros" |
"Hello world", "Foo bar", Another
boring example string" |
Operator Overloading |
And - operator & |
Text.StartsWith("Hello") &
Text.Contains("bar") |
"Hello, Foobar" |
"Hello, World", "Foo bar" |
Or - operator | |
Text.StartsWith("Hello") &
Text.Contains("bar") |
"Hello, Foobar", "Hello, World", "Foo
bar" |
"boring string" |
Not - operator ! |
!Text.StartsWith("Hello") |
"Foo bar", "index.html" |
"Hello, Rhino Mocks" |
Operator Overloading warning: Pay attention to operator precedence if
you are using several operators in a single statement, otherwise you may get
unexpected results.
Creating custom constraints: Creating you custom constraint is easy,
just derive a class from AbstractConstraint and return true from the Eval()
method.
Setup Result:
Sometimes you have a method on your mocked object where you don't care how /
if it was called, you may want to set a return value (or an exception to be
thrown), but for this specific test, you just don't care. For example, you may
have some interaction that you already verified, or you are testing some other
class and just need to get the right value from a method. The way to do it in
Rhino Mocks is to use SetupResult.On().Call(). Here is the code:
[Test]
public void SetupResultUsingOrdered()
{
SetupResult.On(demo).Call(demo.Prop).Return("Ayende");
using(mocks.Ordered())
{
demo.VoidNoArgs();
LastCall.On(demo).Repeat.Twice();
}
mocks.ReplayAll();
demo.VoidNoArgs();
for (int i = 0; i < 30; i++)
{
Assert.AreEqual("Ayende",demo.Prop);
}
demo.VoidNoArgs();
}
When we use SetupResult.On().Call() we tell Rhino Mocks: "I don't care about
this method, it need to do X, so just do it, but otherwise ignore it." Using
SetupResult.On().Call() completely bypass the expectations model in Rhino Mocks.
In the above example, we define demo.Prop to return "Ayende", and we can all it
no matter in what the currently expected method is.
What about methods returning void?
You would use LastCall.On().Repeat.Any(), which has identical semantics (ignore
ordering, etc).
Note: If you want to have a method that can repeat any number of time,
but still obey ordering, you can use:
LastCall.On().Repeat.Times(0,int.MaxValue), this does obey all the normal rules.
Mocking classes:
Rhino Mocks supports mocking concrete classes as well as interfaces. In fact,
it can even mock classes that doesn't have a default constructor! To mock a
class, simply pass its type to MockRepository.CreateMock() along with any
parameters for the constructor.
[Test]
public void AbuseArrayList()
{
using(MockRepository mocks = new MockRepository())
{
ArrayList list = (ArrayList)mocks.CreateMock(typeof(ArrayList));
Expect.On(list).Call(list.Capacity).Return(999);
mocks.ReplayAll();
Assert.AreEqual(999,list.Capacity);
}
}
If you want to call the non default constructor, just add the parameters
after the type. Like this:
ArrayList list = (ArrayList)mocks.CreateMock(typeof(ArrayList),500);
Some things to be aware of:
- You cannot create a mock object from a sealed class.
- You cannot intercept calls to non-virtual methods.
Limitations:
- Currently Rhino Mocks can't mock interfaces and classes that use out or ref
parameters.