Dot Net Rocks
11 October 2008  | 
An Introduction To Mock Objects
DDD5
Photo by John Thomson

Introduction

In this article I’m going to discuss Mock Objects. However, I’m not going to tell you that you should use Mock Object, or that you shouldn’t use mock objects. There seem to be various factions breaking out in the agile world these days. Some advocate the use of Mock Objects, while others suggest that Mock Objects are the work of the Devil himself. And I’ve met both types of person.

I’m just going to tell you what Mock Objects are, where you might find them useful and cite some examples of how to use them.

If you already use unit testing then you are probably aware that certain aspects are difficult to test. How do you, for example, unit test when the code calls into a database? Or test your code when it relies on classes that are incomplete?

This article aims to address these issues and present a way in which these tests become easier to write and the testing framework becomes easier to maintain.

Although this article aims to be general about unit testing with mock objects, the specific examples that I’ll show are in C# using NUnit as the unit testing framework and Rhino Mocks as the mock object framework.

Unit Testing

DSCF3857
Photo by John Thomson

Unit testing is growing in popularity and is fundamental to many, if not all, Agile development practices. Testing a unit of code, a single method for example, can be difficult because of the need to isolate it from the rest of the system. It isn’t always possible to do that.

If you have seen examples of how to unit test code and have attempted to apply that to a system you are writing you’ll quickly discover that most examples of unit testing show fairly simple cases. But in the real world applications are layered and have various subsystems that need to interact with each other.  This is where mock objects come in.

By incorporating unit tests into the development cycle the way the code is written has changed in order to make it easier to unit test. These changes also have a tendency to make the code design more consistent across the project as all code is designed not only to fulfil its primary purpose, but also so that it can be tested easily.

The use of mock objects is just another tool that you can use when unit testing to achieve the goals of writing high quality code.

What are Mock Objects?

At their simplest they are simple objects that mimic the interface of the real thing. However, many mock object frameworks are available that make it easier to create mock objects and detect that it was used correctly.

A mock object can look as simple as this:

public class MyMockObject : ISomeInterface
{
public int PerformComplexCalculation(int a, int b)
{
return 5;
}
}

In most cases a mock object framework will be involved. This will create the mock object and implement its method and properties with the dummy code.

Why use Mock Objects?

As previously mentioned, ensuring that each test operates in isolation is important when unit testing. The use of mock objects ensures that the testing of a method can be done in isolation without it calling classes or services that are peripheral to the unit being tested.

It could be possible to write the test in such a way as to set up the environment in which the test will operate, but this has several disadvantages. Setting up an external environment can take time, both to write the set up code and to achieve the correct initial state each time the test is run. It will also reduce the clarity of the test method by cluttering it with code that exists only to set up the test environment.

Mackinnon, Freeman and Craig in their paper “Endo-testing: Unit Testing with Mock Objects” define seven reasons to use mock objects.

The real object has nondeterministic behaviour

Any class or method that, given the same inputs, performs differently each time it is run is nondeterministic.  A good example may be a random number generator or a method that returns the current time.

A more typical example, from the perspective of writing business software, may be that the database against which you are developing has changing state, especially if it is shared among many developers. If that is the case then it is perfectly possible that a unit test that was working yesterday no longer works today because the contents of the database have changed.

The real object is difficult to set up

Again this will be the case if the code being tested calls into a database. Even if the test is being run against a test database that no one else is using it would have to be reset between each test. This could be a challenge.

It could also be the case if the real object has a complex set of associations and interaction between those associations. It is much easier to mock just the object that the code under test calls and have it return the final value rather than have to set up all those other objects too.

The real object has behaviour that is hard to trigger

Difficult to trigger behaviour would include things like testing the reaction of a piece of code to a network or disk error. It would be difficult to simulate a real network error consistently during a test, so the mock object can be set to throw an exception at the point that you want to simulate the external failure so you can ensure that the code you are testing handles the error correctly.

The real object is slow

Some object, or rather the operations that they carry out are slow. Calling a web service is slow. Calling a database is slow. Unit tests need to be quick so that the developer will run them frequently. If running the unit tests is too slow then the developer won’t run them as frequently and the time between introducing a bug and it being detected increases.

The real object has or is a User Interface

Any interaction with the user will be difficult to replicate consistently in a unit tests. Users, even the developer that wrote the code, will make mistakes when interacting with the program. Tests could fail because of user error rather than faulty code.

I should note at this point that mocking out the user interface is not a replacement for testing the user interface of the application. Users, testers and even the developer that worked on the code, have a built in ingenuity for pressing exactly the wrong button at the wrong time exposing previously unknown bugs. Once the bug is known about a unit test can be written to demonstrate the bug, and once the code is fixed it will act as a beacon should the bug reappear.

The real object uses a call back

Sometimes code may not return a value back to the calling method. It may use a call back to return the information. This is especially true in multithreading or event driven environments. In order for the test to know if the code passed the call back can be mocked so that the test code can examine how and what happened.

The real object does not yet exist

In larger teams, or test first development, it may be the case that you are writing a test for code that uses an object that does not yet exist.

How would I use Mock Objects?

At the simplest level they are just stand in objects that look like the real thing. Typically, this is through a reference to an interface that both the real object and the mock object share, although this does not have to be the case.

For example, in Rhino Mocks the mock object can be set up and used like this:

MockRepository repository = new MockRepository();
IInvoice mockInvoice = repository.CreateMock();
InvoiceDetail detail = new InvoiceDetail();
detail.Init(mockInvoice, standardDescription, 1, 1M, 0.1M);

Some mock object frameworks can inherit from the real class and implement the mock through virtual methods. Personally I don’t like this because it can potentially cause problems if a method or property isn’t declared as virtual and the real code is accessed instead of the mock code, or if a method isn’t mocked, but the test code then attempts to access it. It can be very frustrating chasing down a problem in the code under test only to find it is because the mock object didn’t mock everything as expected. Obviously, if you want to mock only part of an object it can open up the possibility of doing that.

It is important to realise that the unit tests should not test the object being mocked – if they did, all they would be testing is the mock object rather than the real object. Unit tests only test one piece of functionality at a time. Only the unit test and the code under test are genuine, any other code is mocked.

How do mock frameworks work?

Mock frameworks generally allow you to set up the mock object to return the relevant data and ensure that any expectations are met.

Setting up a simple expectation

A test is created. In it a mock object is required, so one is also created. In the code under test a particular method on the mock object needs to be called and it returns a particular value or object. The test asserts the code under test performed correctly, and that the expectations on the mock object are also met.

That explanation is probably a little abstract, so it might be better to actually view a unit test containing a simple expectation.

This unit test will ensure that the NetTotal property on the Invoice class returns the same value as the single detail line passed in. Because the InvoiceDetail object could be difficult to set up a mock object is created to represent it through its public interface.

[Test]
public void NetTotalShouldReturnSameAsSingleDetailLine()
{
// Set up the test environment
MockRepository repository = new MockRepository();
IInvoiceDetail mockDetail = repository.CreateMock();
// Set up the expectations
decimal expectedResult = 1.50M;
Expect.Call(mockDetail.NetTotal).Return(expectedResult);
// Set up the object under test
Invoice invoice = new Invoice();
invoice.Init(1, DateTime.Now);
invoice.AddDetail(mockDetail);
// Run the test
repository.ReplayAll();
decimal actual = invoice.NetTotal;
// Assert that the test conditions are met
Assert.AreEqual(expectedResult, actual, "Expected the result to be 1.50");
// Verify that the mocks were used as expected.
repository.VerifyAll();
}

In the above example the mockDetail object has an expectation set on it. It expects that the property NetTotal will be called. When the test code is running it will then return the value specified.

Once the mock objects are set up and the test is ready to run the mock objects need to be set into replay mode. In this mode the values that were set up earlier will be returned when the methods or properties are called.

Finally, to ensure that the expected method and properties are called during the test VerifyAll() will check.

Should the expectation not be met an error message will be shown in the unit testing framework. For example, say the NetTotal property was never called then a message like this would appear:

Cam.MOSample.Bll.Test.InvoiceTest.NetTotalOnInvoiceShouldReturnSameAsSingleDetailLine : Rhino.Mocks.Exceptions.ExpectationViolationException : IInvoiceDetail.get_NetTotal(); Expected #1, Actual #0.

Expecting more

A second test for NetTotal is to ensure that it sums the collection of details properly. A test could be written like this:

[Test]
public void NetTotalShouldReturnSumOfDetailLines()
{
// Set up the test environment
MockRepository repository = new MockRepository();
IInvoiceDetail mockDetail1 = repository.CreateMock();
IInvoiceDetail mockDetail2 = repository.CreateMock();
IInvoiceDetail mockDetail3 = repository.CreateMock();
// Set up the expectations
decimal itemTotal1 = 1.50M;
decimal itemTotal2 = 2.99M;
decimal itemTotal3 = 4.99M;
decimal expectedResult = 9.48M;
Expect.Call(mockDetail1.NetTotal).Return(itemTotal1);
Expect.Call(mockDetail2.NetTotal).Return(itemTotal2);
Expect.Call(mockDetail3.NetTotal).Return(itemTotal3);
// Set up the object under test
Invoice invoice = new Invoice();
invoice.Init(1, DateTime.Now);
invoice.AddDetail(mockDetail1);
invoice.AddDetail(mockDetail2);
invoice.AddDetail(mockDetail3);
// Run the test
repository.ReplayAll();
decimal actual = invoice.NetTotal;
// Assert that the test conditions are met
Assert.AreEqual(expectedResult, actual, "Expected the result to be 9.48");
// Verify that the mocks were used as expected.
repository.VerifyAll();
}

Rhino mocks allows a method or property to be called multiple times and for that expectation to be set up. For example:

Expect.Call(mockDetail.NetTotal).Return(itemTotal).Repeat.Times(3);

If the NetTotal property is called any number of times other than 3 then the test will fail as the expectation is not met.

It is also possible to set up the mock object to expect that a particular method or property is never called. In this case if the code under test calls that particular method or property on the mock object an exception is immediately raised and the test fails.

Expect.Call(mockDetail.NetTotal).Repeat.Never();

Throwing an exception from a mock object

It is possible to have the mock object throw and exception at a particular point. This is good for testing conditions that would otherwise be difficult to simulate such as database problems or network errors.

However there are some problems with this that has more to do with the way the .NET Framework has been built rather than anything else. For example, say your DAL communicates with SQL Server and you want to mock the DAL and have it simulate some error condition, you cannot throw a SqlException because the constructors are private and the one factory method is internal.

Since of the reasons for building a DAL is to separate the database implementation from the rest of the system it could reasonably be concluded that the rest of the system doesn’t care specifically about SqlExceptions. All database exceptions derive from DbException which is abstract – if any future data provider that might ever be used can guarantee to derived its exception from DbException then that is a candidate to be mocked.

Since the DAL probably ought not to be revealing anything about its implementation to its callers then a more viable, and testable, solution would be to create a DalException class that indicates to callers what is wrong. Any specific errors that the caller needs to be aware of can be translated for the general DalException class and the original exception added as the innerException object so that exception logging code has access to the full stack trace and original message.

[Test]
public void CreateInvoiceShouldThrowExceptionOnBadSqlConnection()
{
// Set up the test environment
DalException expectedException = new DalException();
MockRepository repository = new MockRepository();
IInvoiceDal dal = repository.CreateMock();
// Set up the expectations
int invoiceNumber = 123; 
Expect.Call(dal.GetInvoiceHeader(invoiceNumber)).Throw(expectedException);
// Run the test
repository.ReplayAll();
InvoiceFactory factory = new InvoiceFactory();
IInvoice actual = factory.CreateInvoice(dal, invoiceNumber);
// Assert the conditions are met.
Assert.IsNull(actual, "Expected the factory to return null");
// Verify the mocks were used as expected.
repository.VerifyAll();
}

In the above example an exception object is created and the mock object is told to throw it when GetInvoiceHeader is called. The CreateInvoice method catches the exception and logs the details then returns null.

Mocking object creation

There isn’t actually anything in the Rhino Mocks framework that allows object creation to be mocked.

There are those that believe that object creation should not need to be mocked as all relevant objects should be passed into a method. However, there are patterns that specifically instantiate objects then use them. If the code under test is a factory method then the returned object could be a mock object if the factory supports mock objects. However, the problem is how to provide a consistent interface that returns mock objects when appropriate and real ones at all other times.

For this I’ve created an Object Dispenser class that will dispense objects. When in testing mode it will dispense objects that have been added to its repository, at all other times it creates a real object. The objects in question must implement a specific interface, and it is a reference to this interface that is returned.

Introducing the Object Dispenser

In order for the ObjectDispenser class to work in a test it relies on a conditional compilation symbol “TEST” to be set up. In normal code assemblies this symbol would be absent, in test assemblies this can be easily added.

It does not require that the tests are filled with lots of #if / #endif statements because the Conditional attribute has been added to selected methods in the Object Dispenser. It works in exactly the same way as the System.Diagnostics.Debug class by telling the compiler that any call to the method is to be removed unless the relevant symbol is present. The symbol will be present in the test assemblies but not the real assemblies. That way the developer can be assured that it won’t be possible to accidentally set the Object Dispenser into test mode in production code.

Visual Studio 2005 Project Properties Window

Using the Object Dispenser

From within a test the first thing to do is call StartTesting(). It may be advantageous to call this from within the test setup and teardown methods.

[SetUp]
public void SetUp()
{
ObjectDispenser.StartTesting();
}
[TearDown]
public void TearDown()
{
ObjectDispenser.EndTesting();
}

In the production code the object dispenser can be called on to create the object like this:

IInvoice result = 
ObjectDispenser.DispenseObject(typeof(Invoice));

The method will return a reference to the interface, but a the type of the preferred concrete object is also passed in so that if the object dispenser is not in testing mode it can instantiate and return an object of the correct type. In testing mode the dispenser will return an object that was added earlier.

If many objects need to be created at once then there is a DispenseMultipleObjects method. It works exactly the same was as DispenseObject except that it also takes a count.

And there’s more

In fact there is much more to Rhino Mocks and using mock objects in unit testing. I’ve covered only the most common scenarios here.

Caveats

There are a number of concerns that need to be watched out for when unit testing with Mock Objects.

Unit Testing, in general, will not show up errors that arise from interaction with other components, even with the use of Mock Objects. This is why wider system tests still need to be carried out. The use of Mock Objects within these unit tests can lessen the risk, but it cannot remove it altogether.

It can often be quite difficult to create mock objects for external libraries that were not built with unit testing, or mocking, in mind. In such cases some level of indirection can be employed. For example a DAL would interact directly with the database framework classes that call into the database. In testing the DAL could be mocked rather than the framework classes.

The mock object may return erroneous values to the test. This would be the result of a lack of understanding of what the code being mocked does. For example, a class that represents a road may return that information in metric units, while the tester accidentally setup the mock objects to work in imperial units.

Summary

Mock objects, while not without their critics, are especially useful when the real object or environment is difficult to set up, or has behaviour that is hard to trigger. They provide a stable set of deterministic conditions to be put in place. When the real object is slow they provide speed and it allows code to be tested before the real object is ever created.

In short mock objects can be useful in a variety of locations, but care should be taken not to overuse them.

Downloads

Source Code:
Object Dispenser

Slide Decks:
Power Point 2007

Related Blog Entries
External Links
No responsibility can be made for the content provided by external websites. Links are provided in good faith.

 Jo Dono Suport punt SCO

 A'm haudin at dot SCO

 Tha sinne toirt taie do dot SCO

 I'm supporting dot SCO

Copyright © 2006-2008 Colin Angus Mackay. All Rights Reserved. Privacy StatementTerms Of Use