Wednesday, February 16, 2011

Using RhinoMocks Effectively for Unit-Testing

I'm a big proponent of unit-testing the code I design and build. It allows me to verify the behavior of the production code I write as well as providing a robust regression test suite that stops other developers and myself from breaking functionality we didn't think we would when making code changes. No matter what the language you develop applications in, unit-tests coupled with source control (e.g. Subversion), a continuous integration build system (e.g. Hudson), and some process best practices are indispensable tools for developing quality applications in the an efficient manner.

This is how I have been using RhinoMocks effectively within my C# .NET MSTest unit-tests:

private MockRepository mocks;
private IScheduler schedulerMock;
private IDataStore dataStoreMock;

[TestInitialize] 
public void Setup() 
{ 
     mocks = new MockRepository(); 
     schedulerMock = mocks.DynamicMock<IScheduler>();
     dataStoreMock = mocks.DynamicMock<IDataStore>();
}

[TestCleanup] 
public void CleanUp() 
{ 
     // Verifies that all the expectations on all mock objects in the repository are met. 
     mocks.VerifyAll(); 
}

[TestMethod]
public void StartAndStopTheProcess()
{
     // setup some expectations for methods that should be called
     Expect.Call(schedulerMock.Start);
     Expect.Call(() => schedulerMock.Shutdown(false));
     Expect.Call(dataStoreMock.Save(null).IgnoreArguments();

     // switch the mock repository from record mode into replay mode
     mocks.ReplayAll();
     
     // test the creation of the process and its starting and stoping
     Process process = new Process(schedulerMock, dataStoreMock);
     process.Start();
     process.Shutdown();
}


Here is a checklist when creating a new unit-test class that uses RhinoMocks:
  1. Create private member variables for the RhinoMocks MockRepository object and any other mocks you'll need to mock up and pass into your methods under test
  2. In your TestInitialize method, new up a MockRepository object and create dynamic mocks for each of your mock objects
  3. In your TestCleanup method, call VerifyAll() on the MocksRepository object so that when the unit-test completes, every expectation that you have set will be verified. Note that if an expectation is not met an exception will be thrown at this point by the RhinoMocks framework.
  4. In your unit-test method:
    1. Setup each expectation accordingly deciding whether to ignore arguments, return certain types when called, or anythign else RhinoMocks allows you to do (see this PDF document for a quick reference about the RhinoMocks 3.3 API)
    2. Switch the MockRepository object from record mode to replay mode
    3. Call the particular method you want to unit-test passing in the mocked-up objects you've set expectations on.

No comments: