In the previous article we introduced the basics of mocking. In this article we are going to explore some features of RhinoMocks mocking framework. If you are new to mocking then we highly recommend that you read the previous article or watch the screen cast on mocking.
Introduction:
In the previous article we introduced the basics of mocking. In this article we are going to explore some features of RhinoMocks mocking framework. If you are new to mocking then we highly recommend that you read the previous article or watch the screen cast on mocking.
Explanation:
In this post we are going to explain the differences between CreateMock, DynamicMock and PartialMock in RhinoMocks framework.
When you use CreateMock to create a mock object then you are creating a very strict mock object that has to follow certain rules. This means that it will only do what it is told no more and no less. Let’s take a simple example. We need to build a page which will add a customer. We will be using the MVP pattern to build our application. Let’s first take a look at the view.
public interface IAddCustomerView
{
string FirstName { get; }
string LastName { get; }
string Message { set; }
}
We will also have a repository which will create the customer. Here is the ICustomerRepository interface.
public interface ICustomerRepository
{
void Add(Customer customer);
}
Now, let’s take a look at the presenter which uses the view and the repository to perform action on the view.
public class AddCustomerPresenter
{
private IAddCustomerView _view;
private ICustomerRepository _repository;
public AddCustomerPresenter(IAddCustomerView view, ICustomerRepository repository)
{
_view = view;
_repository = repository;
}
public void AddCustomer()
{
Customer customer = new Customer();
customer.FirstName = _view.FirstName;
customer.LastName = _view.LastName;
_repository.Add(customer);
if(customer.Id > 0)
_view.Message = "Customer has been inserted";
else _view.Message = "Not Inserted";
}
}
The AddCustomer method is responsible for creating a new customer object and then passing it to the repository for the actual insert operation. If the customer is inserted then the id of the customer is changed and hence the message “Customer has been inserted” is assigned to the message property of the view.
The unit test looks something like the following:
[TestFixture]
public class when_customer_is_added
{
private AddCustomerPresenter _presenter;
private ICustomerRepository _repository;
private IAddCustomerView _view;
private MockRepository _mocks;
[SetUp]
public void initialize()
{
_mocks = new MockRepository();
_view = _mocks.CreateMock<IAddCustomerView>();
}
[Test]
public void should_set_the_successfull_message()
{
_presenter = new AddCustomerPresenter(_view, new CustomerRepositoryStub());
using (_mocks.Record())
{
SetupResult.For(_view.FirstName).Return("mohammad");
SetupResult.For(_view.LastName).Return("azam");
Expect.Call(_view.Message = "Customer has been inserted");
}
using (_mocks.Playback())
{
_presenter.AddCustomer();
}
}
}
There is couple of important things about this unit test. First we are using the CreateMock to create a mock object. This means that our mock object will behave exactly as we will expect it to behave. Second we are using CustomerRepositoryStub as an ICustomerRepository. This is because the CustomerRepository needs to perform action on the customer object. This can only be simulated by using a stub. Here is the implementation of the stub.
public class CustomerRepositoryStub : ICustomerRepository
{
public void Add(Customer customer)
{
customer.Id = 12;
}
}
The only function of the CustomerRepositoryStub is to change the customer id so that the message property can be set appropriately.
If you run the above test it will pass. Now, let’s make a small change in your presenter code. We will add only one line which assigns the view some dummy text.
public void AddCustomer()
{
Customer customer = new Customer();
customer.FirstName = _view.FirstName;
customer.LastName = _view.LastName;
_view.Message = "foo";
_repository.Add(customer);
if(customer.Id > 0)
_view.Message = "Customer has been inserted";
else _view.Message = "Not Inserted";
}
If you run the above test then it will fail. This is because the CreateMock creates a strict mock that should only perform what is described in the expectations. You can easily pass the test by making a DynamicMock. Take a look at the code below:
[SetUp]
public void initialize()
{
_mocks = new MockRepository();
_view = _mocks.DynamicMock<IAddCustomerView>();
}
DynamicMock will create weak mock which means that it expects you to fulfill the expectations but if you trigger some other methods then it will not blow up on you. DynamicMock is like a good teacher who does not get mad if you do extra chapters which were not included in the assignment.
Let’s move to PartialMock. PartialMocks can be used to create a mock object of a concrete class. Keep one thing in mind that you can only call virtual methods of the concrete class. Here is a simple example.
public class CustomerArchiveService
{
public virtual void Foo()
{
}
public virtual bool Archive(Customer customer)
{
return false;
}
}
The CustomerArchiveService does not have an interface hence we will use PartialMock to mock the concrete class.
[SetUp]
public void initialize()
{
_mocks = new MockRepository();
_view = _mocks.DynamicMock<IArchiveCustomerView>();
_service = _mocks.PartialMock<CustomerArchiveService>();
}
[Test]
public void should_display_successfull_message()
{
_presenter = new ArchiveCustomerPresenter(_view, _service);
using (_mocks.Record())
{
SetupResult.For(_view.FirstName).Return("Mohammad");
SetupResult.For(_view.LastName).Return("Azam");
Expect.Call(_service.Archive(null)).IgnoreArguments().Return(true);
Expect.Call(_view.Message = "Archived");
}
using (_mocks.Playback())
{
_presenter.Archive();
}
}
Conclusion:
Mocking helps to isolate the unit test from the rest of the system. This makes the test run faster and as an independent entity. CreateMock, DynamicMock and PartialMock are some of the methods contained in the RhinoMock framework which allow to create mock objects.
[Download Sample]
References:
Introduction to Mocking
Screencast: Introduction to Mocking