In 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.
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.
When I was first learning about mocking frameworks (in my case RhinoMocks I found many examples on how to use them. One in particular (this post by Nikola Malovic) gave me the aha moment. This was written some time ago before .Net 3.5 was released.
I wanted to show an example of a unit test before and after implementing a mocking framework. My aim is to show why and how mocking frameworks (including Moq) can help you write unit tests more productively without compromising code readability or maintainability. Don’t worry if you’re new to writing unit tests or mocking frameworks, the examples should be easy enough to follow. If we have any questions just ask.
So here’s my take on it. I have split this post into two parts, stubs and mocks.
Anyway let’s get to the code. The goal is to create a unit test to assert ‘an error message is logged if a exception occurs when a user tries to add an invalid customer to a database’.
The emphasis shouldn’t be on the code per se, the aim is to keep it simple and easy to understand, after all validating objects is common practice! Notice how I’ve used the Arrange, Act and Assert layout so the test is easy to follow.
The code below shows:
Customer object
A interface for the logger
A interface and class for customer validation
A class for the customer service
using System;
namespace RhinoMocksDemo
{
public class Customer
{
public string Name { get; set; }
}
public interface ICustomerValidator
{
bool IsValid(Customer customer);
}
public class CustomerValidator : ICustomerValidator
{
public const string CUSTOMER_NAME_ERROR_MESSAGE = "Customer name cannot be null or empty";
public bool IsValid(Customer customer)
{
if (String.IsNullOrEmpty(customer.Name))
{
throw new ArgumentException(CUSTOMER_NAME_ERROR_MESSAGE);
}
return true;
}
}
public interface ILogger
{
void LogError(string errorMessage);
}
public class CustomerService
{
private readonly ICustomerValidator _customerValidator;
private readonly ILogger _logger;
public CustomerService(ICustomerValidator customerValidator,ILogger logger)
{
_customerValidator = customerValidator;
_logger = logger;
}
public bool AddCustomer(Customer customer)
{
try
{
if (_customerValidator.IsValid(customer))
{
// Add it to the database
return true;
}
}
catch (Exception exception)
{
_logger.LogError(exception.Message);
}
return false;
}
}
}
Notice the separation of concerns in the code as the customer validator and logger are passed (injected) in the constructor. It’s possible to use mocking frameworks without coding this way, and I might post on how to do that, but let’s stick to Uncle Bobs’ SOLID Principles for now.
So if I wasn’t using a mocking framework, I could write a test class that implements the logging interface and pass that into the constructor.
using System.Collections.Generic;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
namespace RhinoMocksDemo
{
public class TestLogger: ILogger
{
public TestLogger()
{
Messages = new List<string>();
}
public List<string> Messages { get; private set; }
public void LogError(string errorMessage)
{
Messages.Add(errorMessage);
}
}
[TestFixture]
public class CustomerProviderTestFixture
{
[Test]
public void Will_Write_To_Log_If_Customer_Fails_Validation_When_Adding_Customer()
{
// Arrange
var customerValidator = new CustomerValidator();
var testLogger = new TestLogger();
var customerService = new CustomerService(customerValidator,testLogger);
var customerWithNoNameThatIsFailVaidation = new Customer();
//Act
customerService.AddCustomer(customerWithNoNameThatIsFailVaidation);
//Assert
Assert.That(testLogger.Messages.Count,Is.EqualTo(1));
}
}
}
The test passes and the good news is we have a unit test that achieves our goal.
However there is a major problem here:
We are to dependent on the validation class – i.e. we had to know how to make it throw an exception
A mocking framework provides us with the ability use a stub instead of an actual object. In the code below you can see the test now uses RhinoMocks to generate (a proxy for) the validation object as a stub. This allows you to control how the stubbed object should behave.
For our example when calling the IsValid method in the code an exception should be thrown.
[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));
}
The unit test still passes and we are no longer dependent on how or what makes the validation class throw an exception.
The most common usage is to stub repositories because you don’t want to depend on a database, especially when it comes to continuous integration builds. The following example shows how to get famous people using a repository stub without ever having to create a concrete implementation of a famous people repository. This makes testing faster and allows you to focus on writing good unit tests for the subject under test.
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Rhino.Mocks;
namespace GettingStartedWithRhinoMocks
{
public class FamousPeople
{
public string Name { get; set; }
}
public interface IFamousPeopleRepository
{
IEnumerable<famousPeople> GetFamousPeople();
}
public class FamousPeopleService
{
private readonly IFamousPeopleRepository _famousPeopleRepository;
public FamousPeopleService(IFamousPeopleRepository famousPeopleRepository)
{
_famousPeopleRepository = famousPeopleRepository;
}
public IEnumerable<famousPeople> NameStartsWithFilter(string searchText)
{
return _famousPeopleRepository.GetFamousPeople().Where(c => c.Name.StartsWith(searchText));
}
}
[TestFixture]
public class FamousPeopleServiceTestFixture
{
[Test]
public void Can_Filter_FamousPeople_Using_Stub_To_Return_FamousPeople()
{
// Arrange
var famousPeopleRepository = MockRepository.GenerateStub<ifamousPeopleRepository>();
var famousPeopleService = new FamousPeopleService(famousPeopleRepository);
famousPeopleRepository.Stub(c => c.GetFamousPeople()).Return(FamousPeopleStub);
// Act
var result = famousPeopleService.NameStartsWithFilter("Bill");
// Assert
Assert.That(result.Count(), Is.EqualTo(3));
}
private List<famousPeople> FamousPeopleStub
{
get
{
return new List<famousPeople>()
{
new FamousPeople(){Name = "Bill Gates"},
new FamousPeople(){Name = "Bill Clinton"},
new FamousPeople(){Name = "Bob Hope"},
new FamousPeople(){Name = "Billy The Kid"}
};
}
}
}
}