I’ve been asked on a few occasions by my colleagues how I approach unit testing with Rhino Mocks; my approach has changed since Rhino Mocks 3.5 with the new Arrange Act Assert (AAA) syntax was released, so I thought I would write about why I use Rhino Mocks and show some example tests.
The reasons I use Rhino Mocks are
- It allows me to do interaction testing (how classes interact with each other)
- I can control the behaviour of any dependency that the class that I’m testing has
- It enables me to test different paths of execution easily.
- I can do proper unit testing, e.g. my tests have no dependency on databases, configuration files or other external resources
Below is the OrderProcess class that I am going to use to demonstrate how to test using Rhino Mocks
public class OrderProcess
{
private readonly IOrderRepository repository;
/// <summary>
/// Constructor
/// </summary>
/// <param name="repository"></param>
public OrderProcess(IOrderRepository repository)
{
this.repository = repository;
this.repository.OrderModified += new EventHandler(HandleOrderModified);
}
/// <summary>
/// Applies a discount to an order if the
/// order cost is greater than 1000
/// </summary>
/// <param name="orderId"></param>
/// <returns></returns>
public Order GetAndApplyDiscount(int orderId)
{
Order order = repository.Get(orderId);
if (order != null)
{
if (order.Cost > 1000)
{
order.Discount = 100;
repository.Persist(order);
}
}
return order;
}
/// <summary>
/// Persists a list of orders
/// </summary>
/// <param name="orders"></param>
public void Persist(List<Order> orders)
{
List<Order> newOrders = orders.Where(order => order.Id == 0).ToList();
List<Order> existingOrders = orders.Where(order => order.Id > 0).ToList();
if (newOrders.Count > 0)
{
repository.Create(newOrders);
}
if (existingOrders.Count > 0)
{
repository.Update(existingOrders);
}
}
/// <summary>
/// Deletes the specified order
/// </summary>
/// <remarks>Throws an OrderDeletionException is order deletion fails</remarks>
/// <param name="order"></param>
public void Delete(Order order)
{
try
{
repository.Delete(order);
}
catch (Exception)
{
throw new OrderDeletionException();
}
}
/// <summary>
/// Handles the OrderModified event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void HandleOrderModified(object sender, EventArgs e)
{
//Code to handled the order modified event
}
}
The OrderProcess class has a single dependency IOrderRepository which is shown below
public interface IOrderRepository
{
event EventHandler OrderModified;
Order Get(int Id);
void Persist(Order order);
void Delete(Order order);
void Create(List<Order> orders);
void Update(List<Order> orders);
}
The tests for the GetAndApplyDiscount method need to test the following cases
- When the repository does not contain the order and returns null
- When the order returned by the repository has a order cost less than 1000
- When the order returned by the repository has a order cost greater than 1000
Below are the unit tests to satisfy the cases listed above
/// <summary>
/// Checks behaviour when order does not exist in the repository
/// </summary>
[TestMethod]
public void GetAndApplyDiscount_When_Order_Does_Not_Exist()
{
//Arrange
IOrderRepository repository = MockRepository.GenerateMock<IOrderRepository>(); //Create mock IOrderRepository so we can control its behaviour
repository.Stub(x => x.Get(25)).Return(null); //Order does not exist so return null
//Act
OrderProcess objectToTest = new OrderProcess(repository);
Order output = objectToTest.GetAndApplyDiscount(25);
//Assert
repository.AssertWasNotCalled(x => x.Persist(Arg<Order>.Is.Anything)); //Make sure Persist was not called at all
Assert.IsNull(output);
}
In the above test I’ve create a mock instance of the IOrderRepository to allow me to control its behaviour, next I have stubbed the IOrderRepository.Get method so that when it is called it returns null. Next I create an instance of the OrderProcess class and call the GetAndApplyDiscount method, and finally I have asserted that the IOrderRepository.Persist method was not called.
/// <summary>
/// Check behaviour when discount should not be applied
/// </summary>
[TestMethod]
public void GetAndApplyDiscount_When_Order_Cost_Less_Than_1000()
{
//Arrange
IOrderRepository repository = MockRepository.GenerateMock<IOrderRepository>(); //Create mock IOrderRepository so we can control its behaviour
Order order = new Order {Cost = 750, Discount = 0};
repository.Stub(x => x.Get(25)).Return(order); //Setup order to be returned by repository
//Act
OrderProcess objectToTest = new OrderProcess(repository);
Order output = objectToTest.GetAndApplyDiscount(25);
//Assert
repository.AssertWasNotCalled(x => x.Persist(order)); //Make sure the order was not persisted
Assert.AreEqual(0, output.Discount);
}
/// <summary>
/// Checks behaviour when discount should be applied
/// </summary>
[TestMethod]
public void GetAndApplyDiscount_When_Order_Cost_Greater_Than_1000()
{
//Arrange
IOrderRepository repository = MockRepository.GenerateMock<IOrderRepository>(); //Create mock IOrderRepository so we can control its behaviour
Order order = new Order {Cost = 1200, Discount = 0};
repository.Stub(x => x.Get(25)).Return(order); //Setup order to be returned by repository
//Act
OrderProcess objectToTest = new OrderProcess(repository);
Order output = objectToTest.GetAndApplyDiscount(25);
//Assert
repository.AssertWasCalled(x => x.Persist(order)); //Make sure the order was persisted
Assert.AreEqual(100, output.Discount); //Make sure the discount was added to the order
}
Below are the Persist method tests which verify the following cases
- New orders are passed to the IOrderRepository.Create method
- Existing orders are passed to the IOrderRepository.Update method
/// <summary>
/// Checks that the behaviour for persisting new orders
/// </summary>
[TestMethod]
public void Persist_When_Orders_New()
{
//Arrange
IOrderRepository repository = MockRepository.GenerateMock<IOrderRepository>(); //Create mock IOrderRepository so we can control its behaviour
List<Order> orders = new List<Order>
{
new Order {Id = 0, Cost = 1200, Discount = 0 },
new Order{Id = 0, Cost = 1500, Discount = 0}
};
//Act
OrderProcess objectToTest = new OrderProcess(repository);
objectToTest.Persist(orders);
//Assert
repository.AssertWasCalled(x => x.Create(Arg<List<Order>>.List.ContainsAll(orders))); //Make sure orders were created
}
/// <summary>
/// Checks the behaviour for existing orders
/// </summary>
[TestMethod]
public void Persist_When_Orders_Already_Exist()
{
//Arrange
IOrderRepository repository = MockRepository.GenerateMock<IOrderRepository>(); //Create mock IOrderRepository so we can control its behaviour
List<Order> orders = new List<Order>
{
new Order {Id = 2, Cost = 1200, Discount = 0 },
new Order{Id = 12, Cost = 1500, Discount = 0}
};
//Act
OrderProcess objectToTest = new OrderProcess(repository);
objectToTest.Persist(orders);
//Assert
repository.AssertWasCalled(x => x.Update(Arg<List<Order>>.List.ContainsAll(orders))); //Make sure orders were updated
}
/// <summary>
/// Checks the behaviour when there is a mix of new and existing orders
/// </summary>
[TestMethod]
public void Persist_When_Mixture_Of_New_And_Existing_Orders()
{
//Arrange
IOrderRepository repository = MockRepository.GenerateMock<IOrderRepository>(); //Create mock IOrderRepository so we can control its behaviour
Order order1 = new Order {Id = 2, Cost = 1200, Discount = 0};
Order order2 = new Order {Id = 0, Cost = 1500, Discount = 0};
Order order3 = new Order { Id = 32, Cost = 1200, Discount = 0 };
Order order4 = new Order { Id = 0, Cost = 1500, Discount = 0 };
List<Order> orders = new List<Order>
{
order1,
order2,
order3,
order4
};
//Act
OrderProcess objectToTest = new OrderProcess(repository);
objectToTest.Persist(orders);
//Assert
repository.AssertWasCalled(x => x.Create(Arg<List<Order>>.List.ContainsAll(new List<Order>{order2, order4}))); //Make the sure correct orders were created
repository.AssertWasCalled(x => x.Update(Arg<List<Order>>.List.ContainsAll(new List<Order> { order1, order3 }))); //Make the sure correct orders were updated
}
In the test above I’ve again mocked the IOrderRepository, then setup a list of orders (some of which are new and some which are existing) created an instance of the OrderProcess class passed the orders to the Persist method. I then assert that both the IOrderRepository’s Create and Update methods are called and verify that the correct orders are passed to each method
Next are the tests for the Delete method which verify
- That the IOrderRepository.Delete method is called
- The OrderDeletionException is thrown if the IOrderRepository.Delete method throws an exception
/// <summary>
/// Checks behaviour when order is deleted
/// </summary>
[TestMethod]
public void Delete_When_Order_Successfully_Deleted()
{
//Arrange
IOrderRepository repository = MockRepository.GenerateMock<IOrderRepository>(); //Create mock IOrderRepository so we can control its behaviour
Order order = new Order();
//Act
OrderProcess objectToTest = new OrderProcess(repository);
objectToTest.Delete(order);
//Assert
repository.AssertWasCalled(x => x.Delete(order)); //Make sure repository deletes the order
}
/// <summary>
/// Checks behaviour when the repository throws an exception
/// when deleting an order
/// </summary>
[TestMethod]
[ExpectedException(typeof(OrderDeletionException))]
public void Delete_When_Order_Deletion_Fails()
{
//Arrange
IOrderRepository repository = MockRepository.GenerateMock<IOrderRepository>(); //Create mock IOrderRepository so we can control its behaviour
Order order = new Order();
repository.Stub(x => x.Delete(order))
.Throw(new Exception("Deletion Failed")); //Throw an exception when repository is called
//Act
OrderProcess objectToTest = new OrderProcess(repository);
objectToTest.Delete(order);
//Assert
}
In the test above which verifies that an OrderDeletionException is thrown if the call to the IOrderRepository.Delete fails, I’ve stubbed the call to the Delete method so that when it is called an Exception is thrown and I’ve used the ExpectedException attribute to make sure the tests throws an OrderDeletionException.
Finally I need to test that the OrderProcess class subscribes to the IOrderProcess’ OrderModified event as shown in the test below
/// <summary>
/// Checks that the order process subscribes to the
/// OrderModified event
/// </summary>
[TestMethod]
public void Constructor_Subscribes_To_OrderModified_Event()
{
//Arrange
IOrderRepository repository = MockRepository.GenerateMock<IOrderRepository>(); //Create mock IOrderRepository so we can control its behaviour
//Act
OrderProcess objectToTest = new OrderProcess(repository);
//Assert
repository.AssertWasCalled(x => x.OrderModified += Arg<EventHandler>.Is.Anything); //Make sure that the OrderModified event has been subscribed to
}
Attached is an example project containing the code from this post.