Rhino Mock(4) Record – Playback // AAA
http://blog.benhall.me.uk/2008/05/rhino-mocks-35-goodbye-record-and.html
Recently, Ayende released Rhino Mocks 3.5 Beta which includes a new Arrange, Act and Assert syntax style. This is a new syntax for creating your stubs and mocks. To take a deeper look into this new syntax, I'm going to migrate an old example I created for my article Beginning to Mock with Rhino Mocks and MbUnit - Part 2 which is a good read if you are new to the world of mock objects. As a quick refresher, the test I created was ensuring that the PaymentProcessor correctly communicated with the PaymentProcessingObject (in the system, this actually talked to PayPal).
[Test]
public void TakePaymentViaPaymentProcessorUsingMockService()
{
MockRepository mocks = new MockRepository();
IPaymentProcessing mockProxy = mocks.CreateMock<IPaymentProcessing>();
using (mocks.Record())
{
Expect.Call(mockProxy.TakePayment(1, 1, 10.0)).IgnoreArguments()
.Return(true);
}
using (mocks.Playback())
{
PaymentProcessor pp = new PaymentProcessor(mockProxy);
bool result = pp.TakePayment(1, 1, 10.0);
Assert.IsTrue(result);
}
}
The code this tested was:
public interface IPaymentProcessing
{ bool TakePayment(int paymentId, int customerId, double amount); }
internal class PaymentProcessor
{
internal IPaymentProcessing wsProxy;
public PaymentProcessor(IPaymentProcessing proxy)
{
wsProxy = proxy;
}
public bool TakePayment(int paymentId, int customerId, double amount)
{
return wsProxy.TakePayment(paymentId, customerId, amount);
}
}
This is a very generic example, but demonstrates Rhino Mocks using the Record (Setup the mock object) and Playback (Test and use) approach to creating mock objects. Now we have our classic test, let's look at the changes for 3.5
CreateMock is Obsolete
The first change is that CreateMock has been marked as obsolete. Instead, we should be using StrictMock, this makes a lot more sense as it better defines the type of mock being created.
IPaymentProcessing mockProxy = mocks.StrictMock<IPaymentProcessing>();
But this still uses the same Record\Playback model.
AAA Syntax - Arrange, Act, Assert
This is where Rhino Mocks 3.5 is really interesting - for me at least. We can now express our mock object is a much cleaner fashion, taking advantage of .Net 3.5 extension methods and lambda questions. Below, is the same test as above but using the Mock object.
[Test]
public void GenerateMock_TakePaymentViaPaymentProcessorUsingMockService()
{
IPaymentProcessing mockProxy = MockRepository.GenerateMock<IPaymentProcessing>(); #1
mockProxy.Expect(x => x.TakePayment(1, 1, 10.0)) #2
.Constraints(Is.Equal(1), Is.Equal(1), Is.Equal(10.0))
.Return(true);
PaymentProcessor pp = new PaymentProcessor(mockProxy);
bool result = pp.TakePayment(1, 1, 10.0);
Assert.IsTrue(result);
mockProxy.VerifyAllExpectations(); #3
}
#1 Here we tell Rhino Mocks to create us a mock object of type IPaymentProcessing.
#2 Next we define our mock. Here, we are saying we expect TakePayment to be called, we then add some constraints about what the parameters passed in much be, finally defining that true be returned when this is called.
#3 Finally, we verify the exceptions we set in #2 where correct.
I find this new approach to be much easier to read, explain and write. The first time I tried this, I actually mistaken Constraints for Return and as such the following exception was thrown.
mockProxy.Expect(x => x.TakePayment(1, 1, 10.0)).Constraints(Is.Equal(true)); = failed: System.InvalidOperationException : The number of constraints is not the same as the number of the method's parameters!
Just be aware of these new constraints on the parameters.
But, not only can we use this for creating mocks, but we can also create Stubs.
[Test]
public void GenerateStub_TakePaymentViaPaymentProcessorUsingMockService()
{
IPaymentProcessing stubProxy = MockRepository.GenerateStub<IPaymentProcessing>(); #1
stubProxy.Stub(action => action.TakePayment(1, 1, 10.0)).Return(true); #2
PaymentProcessor pp = new PaymentProcessor(stubProxy);
bool result = pp.TakePayment(1, 1, 10.0);
Assert.IsTrue(result);
}
#1 Generate the stub
#2 Define the stub
How cool is that!! Two lines of code to create our stub!
[Test]
public void GenerateStub_AssertWasCalled_TakePaymentViaPaymentProcessorUsingMockService()
{
IPaymentProcessing stubProxy = MockRepository.GenerateStub<IPaymentProcessing>();
stubProxy.Stub(action => action.TakePayment(1, 1, 10.0)).Return(true);
PaymentProcessor pp = new PaymentProcessor(stubProxy);
bool result = pp.TakePayment(1, 1, 10.0);
Assert.IsTrue(result);
stubProxy.AssertWasCalled(x => x.TakePayment(1, 1, 10.00)); #1
}
#1 With stubs, we can also verify that the method was called. Notice this AssertWasCalled is an extension method Rhino Mocks has added to the interface. gain, this is helping with the readability and demonstrates an excellent use of Extension Methods.
If AssertWasCalled failed, then the following exception would be thrown and the test would fail.
Expected that IPaymentProcessing.TakePayment(1, 1, 10); would be called, but is was it was not found on the actual calls made on the mocked object
I think the new AAA syntax is really cool. I used it to explain mock objects for my NxtGenUG Coventry session and upgrading my mock objects was a simple task and made the tests much easier to read. Really looking forward to the final release.