Wednesday, April 19, 2006

Mocking Basics

Introduction


As an XP team that uses NUnit and Continuous Integration, we had the problem that our unit tests ended up being integration tests, and any error would propagate causing many tests to fail and making it hard to find where the original error occurred.
To solve this problem we used NMock to mock out any external dependencies that a class has, therefore making our unit tests insular. However, there are several patterns that we have to use to make this work effectively, and this is the topic addressed in this document.

NMock


NMock is a dynamic mock-object library for .NET. Its purpose is to provide a clean API for rapidly creating mock implementations of custom objects and verifying that objects interact with them correctly from unit tests.

What is a mock?


A mock:
takes on the interface of another object, allowing it to be substituted for a real one for testing purposes.
allows Expectations to be set up, specifying how the class under test is expected to interact with the mock.
fails the test if any of the expectations are violated.
can also act as a stub, allowing the test to specify objects to be returned from mocked methods.

Defining a Dynamic Mock


A dynamic mock sets up a dynamic proxy that implements a given interface, or overrides any virtual methods on a given object type. It is initialised as follows:
IMock mockObject = new DynamicMock( typeof( Interface ));
This mock object can take the place of the real object in unit tests and allow you to control the way that the mock behaves and verify the way the object you are testing interacts with it.

Setting Up a Mock


In order to verify the way that the object under test interacts with our mock we can set up expectations on our mock object.
mockObject.Expect("MethodOnMock", param1, param2, ...);
Here we tell the mock to expect a call to method MethodOnMock with the given parameters. An extension to this is the ability to return an object when a method is called.
mockObject.ExpectAndReturn("MethodOnMock", returnObject, param1, param2, ...);
Here we instruct our mock to return returnObject when MethodOnMock is called. There are several other expectations that you can set up, which we cover in the Appendix.

Verifying a Mock


Once you have run the scenario that should fulfil the expectations that you have set up on the mock object, you have to verify that this is the case.
mockObject.Verify();
If the expectations have not been fulfilled this will fail the test.

Passing a mock to a method


Pattern 1


Whenever the class that you are testing instantiates an external class, always acquire it through a virtual property or method; eg

public void Method() {
Interface object = ObjectInstance;
}

protected virtual Interface ObjectInstance {
get{ return new Object(); }
}

This means that whenever you interact with an external class, in the test you can override this property or method and return a mock object in its place. The simplest way of doing this is passing your mock into the constructor of your test extension and storing it for your overridden property or method to return. The unit test code will look something like this:

[TestFixture]
public class TestClass {

private ClassTestExtension _testObject;
private IMock _mockObject;

[SetUp]
public void SetUp() {
_mockObject = new DynamicMock( Interface );
_testObject = new ClassTestExtension((Interface)mockObject.MockInstance);
}

[Test]
public void SampleTest() {
_mockObject.Expect("Method");
_testObject.MethodUnderTest();
_mockObject.Verify();
}

private class ClassTestExtension {

Interface _mockObject;

public ClassTestExtension( Interface mockObject ) {
_mockObject = mockObject;
}

protected override Interface ObjectInstance {
get{ return _mockObject; }
}
}
}

Rules of thumb


If a class depends on any other class to perform any of its tasks then do the following:
In the NUnit test, derive from the class you are testing to give you control over the external classes it uses.
In the base implementation of the class you are testing, every time you need to use an external class, get it via a virtual property.
If you own the external classes, make it implement an interface to aid with mocking.
In the inherited test class, override these properties to return mock instances of these classes.
In the derived class, overload the constructor to provide one that takes the required mock objects and stores them as member variables so that they can be returned by the properties.
In the setup of the test, create a new instance of the derived class and all the mock objects, passing the mock objects into the constructor. Store all of these objects as member variables.
In each test set up the mock objects (ExpectAndReturn etc) to provide the scenario that you want.
Call Verify on your mock objects at the end of each test.

No comments: