Page History: Rhino Mocks 3.5
Compare Page Revisions
Page Revision: 2008/08/12 17:36
Rhino Mocks 3.5 introduce some new concepts, mostly by building on top of the language features of C# 3.0.
User on the 2.0 platform needs not to worry, there are enough goodies for them as well (inline constraints, for example), but the focus of this document is on documenting the use of Rhino Mocks using C# 3.0.
§§§TocTitlePlaceHolder§§§
EditWhat's New in Rhino Mocks 3.5
New things in Rhino Mocks 3.5:
- Arrange, Act, Assert model
- Lambda and C# 3.0 extensions
- Inline constraints
- Support for mocking interface in C++ that mix native and managed types.
- Allow a mock object to return to record mode without losing its expectations
- CreateMock was deprecated in favor of StrictMock
- Better error handling in edge cases.
- Fixed an issue with mocking internal classes and interfaces
- New event raising syntax
EditUsage Guidance
In general, I recommend following the "Test only one thing" per test. As such, the recommended approach for using Rhino Mocks (and mocking in general) is composed of a single mock object per test and several stubs (as needed).
EditThe difference between stubs and mocks
You can get the actual definition of the these terms in this article:
Mocks Aren't Stubs. I want to focus on the difference from the point of view of Rhino Mocks.
A mock is an object that we can set expectations on, and which will verify that the expected actions have indeed occurred.
A stub is an object that you use in order to pass to the code under test. You can setup expectations on it, so it would act in certain ways, but those expectations will never be verified. A stub's properties will automatically behave like normal properties, and you can't set expectations on them.
If you want to verify the behavior of the code under test, you will use a mock with the appropriate expectation, and verify that.
If you want just to pass a value that may need to act in a certain way, but isn't the focus of this test, you will use a stub.
IMPORTANT: A stub will never cause a test to fail.
Let us create a sample test, which will give us more context to talk about.
We want to write a test for sending an SMS for a forgotten password.
The code should:
- Get the user from the repository
- Reset the password to a random one
- Save the user with the new password
- Send an SMS with the new password
Note that I am using the new syntax here which will be explained shortly
First, we want to test that we reset the password, so we will write the following test:
public void When_user_forgot_password_should_reset_password()
{
var stubUserRepository = MockRepository.GenerateStub<IUserRepository>();
var stubbedSmsSender = MockRepository.GenerateStub<ISmsSender>();
var theUser = new User{HashedPassword = "this is not hashed password"};
stubUserRepository.Stub(x => x.GetUserByName("ayende")).Return(theUser);
var controllerUnderTest = new LoginController(stubUserRepository, stubbedSmsSender);
controllerUnderTest.ForgotMyPassword("ayende");
Assert.AreNotEqual("this is not hashed password", theUser.HashedPassword);
}
In this test, we have created two stubs, passed them to the code under test, and asserted that the password was changed. To make this code pass we can use:
public void ForgotMyPassword(string username)
{
var user = users.GetUserByName(username);
user.HashedPassword = "new pass";
}
Now we want to verify that the user will save the password, so we have the following test:
public void When_user_forgot_password_should_save_user()
{
var mockUserRepository = MockRepository.GenerateMock<IUserRepository>();
var stubbedSmsSender = MockRepository.GenerateStub<ISmsSender>();
var theUser = new User{HashedPassword = "this is not hashed password"};
mockUserRepository.Stub(x => x.GetUserByName("ayende")).Return(theUser);
mockUserRepository.Expect( x => x.Save(theUser) );
var controllerUnderTest = new LoginController(mockUserRepository, stubbedSmsSender);
controllerUnderTest.ForgotMyPassword("ayende");
mockUserRepository.VerifyAllExpectations();
}
Now we have used a mock object and set an expectations on it. Note that even though we are using a mock object here, we are still stubbing the call to GetUserByName. We don't care about this, since this is not what we are testing.
The code under test now looks like:
public void ForgotMyPassword(string username)
{
var user = users.GetUserByName(username);
user.HashedPassword = "new pass";
users.Save(user);
}
Another way to write the test would have been:
public void When_user_forgot_password_should_reset_password()
{
var stubUserRepository = MockRepository.GenerateStub<IUserRepository>();
var stubbedSmsSender = MockRepository.GenerateStub<ISmsSender>();
var theUser = new User{HashedPassword = "this is not hashed password"};
stubUserRepository.Stub(x => x.GetUserByName("ayende")).Return(theUser);
var controllerUnderTest = new LoginController(stubUserRepository, stubbedSmsSender);
controllerUnderTest.ForgotMyPassword("ayende");
stubUserRepository.AssertWasCalled( x => x.Save(user));
}
We have no expectations in this test, just setting the required state and asserting on the actions that occurred when the code under test run.
The difference between the two test is subtle, but important. In most cases, you should prefer to use stubs. Only when you are testing complex interactions would I recommend to use mock objects.
If you need to ensure ordering, or expect to get a complex method, or want to ensure an exact adherence to the expectations.
Note that so far, we haven't even cared about the actual sending on an SMS, let us write a test that ensure that we can send an SMS.
public void When_user_forgot_password_should_reset_password()
{
var stubUserRepository = MockRepository.GenerateStub<IUserRepository>();
var stubbedSmsSender = MockRepository.GenerateStub<ISmsSender>();
var theUser = new User{HashedPassword = "this is not hashed password", Phone = "1234-1234"};
stubUserRepository.Stub(x => x.GetUserByName("ayende")).Return(theUser);
var controllerUnderTest = new LoginController(stubUserRepository, stubbedSmsSender);
controllerUnderTest.ForgotMyPassword("ayende");
stubbedSmsSender.AssertWasCalled( x => x.Send(
Arg.Is.Eqaul("1234-1234"),
Arg.Text.StartsWith("Password was changed to:")
));
}
Here we are asserting that the method was called, and use the inline constraint support to ensure we pass the correct arguments. The code under test now looks like:
public void ForgotMyPassword(string username)
{
var user = users.GetUserByName(username);
user.HashedPassword = "new pass";
users.Save(user);
smsSender.Send(user.Phone, "Password was changed to: new pass");
}
It is important to remember that we don't want to be tied down by our tests. We want to make sure that even if we modify the code under test, we will not break any unrelated tests.
EditCreateMock is deprecated, replaced by StrictMock. The use of Strict Mock is discouraged.
In Rhino Mocks 3.4 and previous, the "default" way to get a mock object was to call
mockRepository.CreateMock<ISmsSender>().
That caused a very serious issue.
CreateMock() would return a strict mock, and as we have already discussed, an overly strict mock will create a brittle test.
As a result of that,
CreateMock() was deprecated and will generate a compiler warning if used.
You can replace calls to
CreateMock() with
StrictMock().
However, using
StrictMock() is discourage. Strict mocks will fail if something that is not expected will happen to them. In the long run, this means that any change to the code under test can break your tests, even if the change has nothing to do with what you are actually testing in this specific test.
I encourage the use of stubs and dynamic mocks instead.
EditMocking with and without an instance of MockRepository
Rhino Mocks 3.5 introduce a new mode of mocking, using mocks without creating the repository.
The purpose of that is to reduce the amount of book keeping that you have to do.
Here is a test without the repository:
public void When_user_forgot_password_should_save_user()
{
var mockUserRepository = MockRepository.GenerateMock<IUserRepository>();
var stubbedSmsSender = MockRepository.GenerateStub<ISmsSender>();
var theUser = new User{HashedPassword = "this is not hashed password"};
mockUserRepository.Stub(x => x.GetUserByName("ayende")).Return(theUser);
mockUserRepository.Expect( x => x.Save(theUser) );
var controllerUnderTest = new LoginController(mockUserRepository, stubbedSmsSender);
controllerUnderTest.ForgotMyPassword("ayende");
mockUserRepository.VerifyAllExpectations();
}
With a repository, the test would look like:
public void When_user_forgot_password_should_save_user()
{
var mocks = new MockRepository();
var mockUserRepository = mocks.DynamicMock<IUserRepository>();
var stubbedSmsSender = mocks.GenerateStub<ISmsSender>();
using(mocks.Record())
{
var theUser = new User{HashedPassword = "this is not hashed password"};
mockUserRepository.Stub(x => x.GetUserByName("ayende")).Return(theUser);
mockUserRepository.Expect( x => x.Save(theUser) );
}
using(mocks.Playback())
{
var controllerUnderTest = new LoginController(mockUserRepository, stubbedSmsSender);
controllerUnderTest.ForgotMyPassword("ayende");
}
}
There are a few other differences between the two approaches:
- Mocks/stubs returned from
MockRepository.GenerateMock() and MockRepository.GenerateStub() are returned in replay mode, and do not require explicit move to replay mode. - Only Dynamic Mocks and Stubs are available without creating the repository. (This is done under the assumption that those are the most commonly used, and deserve their shortcut).
- Without creating a repository, you need to call VerifyAllExpectations() on each of the mock objects that you created. If you have a repository, you can simply call VerifyAll() on the repository and it will do the work for you. Since in most situations you will have only a single mock per test (and stubs require no verifications), that is not an issue.
EditArrange, Act, Assert
The major feature in Rhino Mocks 3.5 is the AAA syntax.
Arrange, Act, Assert is a classic way to setup your tests. First you arrange the state, then you execute the code under test, finally you assert that the expected state change happened.
With interaction based testing, this was generally impossible, and required that you would use the Record/Replay model.
This was a common case for confusion for people new to interaction based testing.
The AAA syntax solve this problem.
Let us take a look at a simple test and analyze it using the AAA approach:
public void When_user_forgot_password_should_reset_password()
{
// arrange
var stubUserRepository = MockRepository.GenerateStub<IUserRepository>();
var stubbedSmsSender = MockRepository.GenerateStub<ISmsSender>();
var theUser = new User{HashedPassword = "this is not hashed password"};
stubUserRepository.Stub(x => x.GetUserByName("ayende")).Return(theUser);
// act
var controllerUnderTest = new LoginController(stubUserRepository, stubbedSmsSender);
controllerUnderTest.ForgotMyPassword("ayende");
// assert
stubUserRepository.AssertWasCalled( x => x.Save(user));
}
We can easily see the different stages of the test.
The main supporters for this method of operation are the Expect() and Stub() methods.
EditExpect and Stub Extension Methods
The Expect() and Stub() methods are extension methods that become available for use on any mock object when you reference (
using in C# or
Imports in VB.NET) the Rhino.Mocks namespace.
EditExpect() Extension Method
The Expect() extension method creates a new expectation for this mock which must be verified/asserted later (using either the VerifyAllExpectations() or AssertWasCalled() methods).
Expect() functions similarly to the Expect.Call() functionality from previous versions of Rhino Mocks, except the .Expect() extension method can be used without a Record() block (whereas Expect.Call() must be called from within an explicit Record() block).
Consider the following example usage which ensures that the controller
SignOut action calls the
authSvc's SignOut method:
Test
public void should_sign_out_of_the_auth_service_when_the_logout_action_is_invoked()
{
IAuthService authSvc = MockRepository.GenerateMock();
authSvc.Expect(svc => svc.SignOut()).Return(true);
SignoutControllercontroller = new SignoutController(authSvc);
controller.SignOut();
authSvc.VerifyAllExpectations();
}
EditStub() Extension Method
The Stub() extension method will stub a specific member so that it will perform the default stub behavior (i.e. not fail the test --
for specific behavior, please see definition of stub earlier in this document) for that particular member.
Consider the following example which is the same as the previous example, but includes an unrelated dependency that may need to be satisfied in order to test the functionality in which we're interested:
Test
public void should_sign_out_of_the_auth_service_when_the_logout_action_is_invoked()
{
IAuthService authSvc = MockRepository.GenerateMock();
IUnrelatedDependency otherDependency = MockRepository.GenerateMock();
otherDependency.Stub(o => o.UserIsLoggingOut());
authSvc.Expect(svc => svc.SignOut()).Return(true);
SignoutController controller = new SignoutController(authSvc, otherDependency);
controller.SignOut();
authSvc.VerifyAllExpectations();
}
EditWhat is the difference between GenerateStub and GenerateMock
GenerateMock
() is equivalent to calling DynamicMock() on a MockRepository instance in previous versions of Rhino Mocks. GenerateMock will create a dynamic mock (as opposed to a static mock or stub).
GenerateStub() is equivalent to calling Stub() on a MockRepository instance in previous versions of Rhino Mocks. GenerateStub will create a stubbed instance which will have stub behavior (see earlier in this article for the definition of Stub and explanation of Stub semantics).
EditUsing the AAA syntax in C# 2.0
EditHow to raise events
Rhino Mocks 3.5 introduces a new way of raising events from mocks. Prior to 3.5 this was generally done via the IEventRaiser interface like this:
[Test]
public void RaisingEventOnView()
{
IView view = mocks.CreateMock();
view.Load+=null;//create an expectation that someone will subscribe to this event
LastCall.IgnoreArguments();// we don't care who is subscribing
IEventRaiser raiseViewEvent = LastCall.GetEventRaiser();//get event raiser for the last event, in this case, View
mocks.ReplayAll();
Presenter p = new Presenter(view);
raiseViewEvent.Raise();
Assert.IsTrue(p.OnLoadCalled);
}
Rhino Mocks 3.5 provides some extension methods to make this a bit easier:
[Test]
public void RaisingEventOnViewUsingExtensionMethod()
{
IView view = mocks.CreateMock();
Presenter p = new Presenter(view);
view.Raise(x => x.Load += null, this, EventArgs.Empty);
Assert.IsTrue(p.OnLoadCalled);
}
The Raise() extension method takes a subscription to the event you want raised (view.Load in this case, as per the x => x.Load += null subscription), the event sender, and the arguments for the event.