How to write better unit tests using RhinoMocks and mocking
TweetIn the last post we saw how the use of stub functionality of a mocking framework within a unit test. The aim of this post is build upon this and look at mocking functionality.
If you find yourself having to create a class to hold ‘state’, like this
using System.Collections.Generic;
namespace GettingStartedWithRhinoMocks
{
public class TestLogger : ILogger
{
public TestLogger()
{
Messages = new List<string>();
}
public IList<string> Messages { get; private set; }
public void LogError(string errorMessage)
{
Messages.Add(errorMessage);
}
}
}
And your test looks something like this
[Test]
public void Will_Write_To_Log_If_Customer_Fails_Validation_When_Adding_Customer_Using_Stub()
{
// Arrange
ICustomerValidator customerValidator = MockRepository.GenerateStub<icustomerValidator>();
var testLogger = new TestLogger();
var customerService = new CustomerService(customerValidator, testLogger);
customerValidator.Stub(c => c.IsValid(null))
.IgnoreArguments() // Use this because the argument is not important
.Throw(new ArgumentException(CustomerValidator.CUSTOMER_NAME_ERROR_MESSAGE)); // This will simulate the throwing of an exception
//Act
customerService.AddCustomer(new Customer());
//Assert
Assert.That(testLogger.Messages.Count, Is.EqualTo(1));
}
You can eliminate the need for the TestLogger class by using a mock like this
[Test]
public void Will_Write_To_Log_If_Customer_Fails_Validation_When_Adding_Customer_Using_Stub_And_Mock()
{
// Arrange
ICustomerValidator customerValidator = MockRepository.GenerateStub<icustomerValidator>();
ILogger logger = MockRepository.GenerateMock<ilogger>();
var customerService = new CustomerService(customerValidator, logger);
customerValidator.Stub(c => c.IsValid(null))
.IgnoreArguments() // Use this because the argument is not important
.Throw(new ArgumentException(CustomerValidator.CUSTOMER_NAME_ERROR_MESSAGE)); // This will simulate the throwing of an exception
// Act
customerService.AddCustomer(new Customer());
// Assert
logger.AssertWasCalled(l => l.LogError(CustomerValidator.CUSTOMER_NAME_ERROR_MESSAGE));
}
The difference is how we assert the exception was logged. The extension method ‘AssertWasCalled’ ensures that the ‘LogError’ method of a logger was called with the correct error message.
If we changed the assert to this
//Assert
logger.AssertWasCalled(l => l.LogError("Oh no"));
The test would fail and RhinoMocks would throw the following exception
Rhino.Mocks.Exceptions.ExpectationViolationException: ILogger.LogError("Oh no"); Expected #1, Actual #0.
So over the two posts we have refactored the unit tests from not using a mocking framework to using both stub and mock functionality of a mocking framework and still have unit tests which are easy to read and maintain.
Martin Fowlers’ article discusses the two styles testing. I recommend you choose which is better on a test by test basis.
The code is available at http://code.google.com/p/arrangeactassert/. The solution is in the GettingStartedWithRhinoMocks folder.
Pingback: Getting started – a beginners guide to how RhinoMocks can help writing unit tests – Part 1 Stubs | Arrange Act Asssert
Pingback: How to write better unit tests using RhinoMocks and stubs | Arrange Act Asssert