Ayende @ Wiki

Edit

Overview

PersistentMockRepository makes it possible to debug unit tests involving mocks of legacy objects with profuse interfaces. Unit tests with such mocked object run noticeably slower during debugging, and have been known to freeze and crash MSVS2005 debugger.

Edit

Usage

Here's a standard Rhino.Mocks unit test:
MockRepository mocks = new MockRepository();
IDemo demo = mocks.CreateMocks<IDemo>();
mocks.ReplayAll();
...do stuff with mocked object, "demo"...


PersistentMockRepository is a singleton cache that extends MockRepository. For simplicity, let's assume that you have one unit test. Here's how you'd use PersistentMockRepository(). In the following example, the cache will be created automatically whenever a dependent assembly changes:

PersistentMockRepository mocks = new PersistentMockRepository ();
IDemo demo = mocks.CreateMocks<IDemo>();
mocks.ReplayAll();
if (!PersistentMockRepository.IsAssemblyLoadedFromCache && !PersistentMockRepository.IsAssemblySaved) {
  mocks.SaveAssembly();
}
...do stuff with mocked object, "demo"...


The above example works well for multiple unit tests provided that you've separated your unit test project(s) from your production project(s) and the cached interfaces are all in production code. This will let you iterate rapidly on unit test development by using the cache.

A more complicated scenario is that you will have multiple unit tests and that some of the mock objects are for legacy code, while other mock objects are for currently changing code. In such instances you should separate your mock objects into two categories:
  • Active Mocks (mocks of things that change)
  • Legacy Mocks (mocks of things that don't change)

Edit

Active Mocks

For mocking actively changing interfaces, just continue to use MockRepository in the standard way. Edit

Legacy Mocks

Create a LegacyMockFactory to manage your PersistentMockRepository. The LegacyMockFactory should:
  • ideally be in a separate project so that you can eliminate all dependencies on actively changing code.
  • be called for all accesses to mocked legacy objects.
  • create a new assembly cache as required:
if (!PersistentMockRepository.IsCacheValid()) {
   ...{create legacy mock objects for all legacy types}...
   PersistentMockRepository.SaveAssembly();
}
...{use legacy mock objects}...
The job of the LegacyMockFactory is to create the cached assembly BEFORE its first use in any unit test. Creating the cached assembly requires caching the proxy code for all of the mocked legacy types, not just the ones needed for a particular unit test. Your LegacyMockFactory will manage the creation.

Edit

Invalidating the cache

There certain circumstances which will cause the cached legacy mock assembly to become invalid:
  • The cache is automatically invalidated when the legacy interfaces change. No action is required, because the cache will automatically invalidate itself when it detects that the dependent legacy dlls have changed.
  • You will need to invalidate the cache when unit tests use additional legacy interfaces that are currently not cached. It's hard to handle this automatically, but it can easily be handled with an Explicit unit test that invalidates the cache:
[Test, Explicit]
public void InvalidatePersistentMockRepository() {
   PersistentMockRepository.InvalidateCache();	
}


Edit

Q & A

Q: Where can I get PersistentMockRepository?
A: We're currently testing out the API and implementation. Click here for latest status

Q: How does PersistentMockRepository track dependent dlls?
A: PersistentMockRepository uses PersistentMockRepository.xml to track the timestamps of DLL's referenced by the cache. Any change (older/newer) to the timestamp of any DLL will invalidate the cache.

ScrewTurn Wiki version 2.0 Beta. Some of the icons created by FamFamFam.