A cacophony of ramblings from my potpourri of notes
 Wednesday, September 03, 2008
Mock Object Frameworks: NMock, RhinoMock, TypeMock

Recently I wanted to select a mock object framework for a new project I am starting on.  As an early developer (it wasn't really key to the customer) spike, I decided to investigate different .NET mock object libraries: NMock, Rhino.Mocks, and TypeMock.

To appreciate these differences, consider the code samples in which the goal is to mock out a "stock provider" class (or interface) that returns stock prices (which cannot be controlled so they are tough to test).  Although the code appears to go from simpler to more complex,  that is simply because what TypeMock accomplishes more with less impact on the target API.  Each tool can handle the code semantics of the previous tool.

NMock (2.0 Release Candidate 2, Jan 31, 2008)

Requires the object to be mocked so support an interface.  Mock objects are generated at runtime to implement the interface.  Programmatically you inject method implementations on the mock object so that it expects certain calls and returns values for those calls.  This works great until the class you want to mock does not implement an interface.  In addition, you will need to code your API such that it allows for tests to pass in the mock objects for the target API to use.
[Free]

Mockery mocks = new Mockery(); 
IStockProvider provider = mocks.NewMock<IStockProvider>(); 
// Notice how it is necessary to pass the mock object's interface into the target.
StockService stockService = new StockService(provider);
Expect.Once.On(provider). Method("GetCurrentPrice"). With("ITRI"). Will(Return.Value(106.20));
Assert.AreEqual<float>(106.20, stockService.GetCurrentPrice("ITRI")); 

Rhino.Mocks (3..5 Release Candidate for .NET 3.5, June 28, 2008)

In addition to all the NMock provides, Rhino.Mocks allows for generating a mock object of a class without the interface a requirement.  What is powerful about this technique, is that you can request Rhino.Mock to create a mock object of a class (even something like System.Net.WebClient) and then pass that mock object into the implementation of your class.  Now any calls to System.Net.WebClient will be to your mock object even though the target library was compiled to use System.Net.WebClient.  Furthermore, the setting up of method call expectations is compiled, rather than just string names for the methods.  Like NMock, however, the target API needs to allow for the test to pass in the mock object you wish for the target to use.  If the target instantiated the mock object directly, then no mock object interception would be available.
[Free]

MockRepository repository = new MockRepository(); 
StockProvider provider = mocks.CreateMock<StockProvider>(); 
// Notice how it is necessary to pass the runtime generated mock object (not the interface) into the target.
StockService stockService = new StockService(provider);// Notice that the method call is compiled rather than just a string. 
Expect.Call(provider.GetCurrentPrice("ITRI").Return(106.20));
Assert.AreEqual<float>(106.20, stockService.GetCurrentPrice("ITRI"));repository.VerifyAll(); 

TypeMock (Typemock Isolator 5.0)

In addition to all the functionality of Rhino.Mocks, TypeMock allows for the runtime generation of type objects on the fly within the target project, not just within the test.  As a result, you can identify the types you wish TypeMock engine to intercept and then, whenever the target library instantiates those objects, a mock object will be created instead.  This allows you to code the target library without any API changes that would make testing easier but not necessarily improve the API
[Commercial][Free (for open source projects)]

MockManager.Init();
using (RecordExpectations recorder = new RecordExpectations())
{
    StockProvider provider = new StockProvider();    // As with Rhino.Mocks, the expected calls are compiled. 
    recorder.ExpectAndReturn(provider.GetCurrentPrice("ITRI"), 106.20);
}
// Notice how it is not necessary to pass the runtime generated mock object.  The target can just 
// call create StockProvider and the call will be intercepted with a mock object instantiation.
StockService stockService = new StockService();Assert.AreEqual<float>(106.20, stockService.GetCurrentPrice("ITRI"));
MockManager.Verify();

To grok the differences, I encourage you to read the comments to understand the subtle yet significant differences.


Wednesday, September 03, 2008 11:42:04 AM (Pacific Standard Time, UTC-08:00)  #    Comments [3]  Computer Related | .Net | Headlines