Mocking the TableGateway pattern
I've been writing some new Hands On Labs for the WCF REST Starter Kit and as I mentioned previously on my blog I want to include unit tests. Several people commented on my previous efforts and pointed out that the tests I included with the REST Collection lab are really integration tests because they use the HTTP stack to pass messages from a client to the REST Service.
So this time I've decided to build out both an integration test layer and a unit test layer. My goal for the unit test layer was to exercise the service without the HTTP stack or the database. What this means is that I had to come up with a way to mock the database layer.
In my previous code I was using static methods on the CohoDB class backed up by LINQ to SQL classes. This time I took a new approach that uses the Table Gateway pattern with a twist. I wanted to use the Factory pattern with the gateway and provide a way to substitute the factory with a Mock factory that I could use when unit testing. Sound confusing? Here is the pattern.
First you need to define an interface for the TableGateway
public interface IWineGateway
{
WineData GetWine(int wineID);
IEnumerable> GetWines(int startIndex, int pageSize);
IEnumerable> GetAllWines();
WineData AppendWine(WineData wineData);
WineData UpdateWine(int wineID, WineData wineData);
WineData PutWine(int wineID, WineData wineData, out bool inserted);
void DeleteWine(int wineID);
}
Next you need an interface for the Factory
public interface IWineGatewayFactory
{
IWineGateway CreateWineGateway();
}
Now you need to implement your interfaces, the factory and the table gateway with classes. To make it simple to create the table gateway I use a static property that defines the factory that will be used (it defaults to the factory that creates the real class) and a static method to create the gateway.
public class WineGateway : IWineGateway
{
static IWineGatewayFactory _factory;
public static IWineGatewayFactory Factory
{
get
{
// Default to the WineGatewayFactory
if (_factory == null)
{
_factory = new WineGatewayFactory();
}
return _factory;
}
set
{
// Unit tests can set a MockFactory if they like
_factory = value;
}
}
public static IWineGateway Create()
{
return Factory.CreateWineGateway();
}
Now when I need a table gateway I can call the static Create method and get one. However it still wasn't quite as convenient as I wanted so in the consuming service I added a property of type WineGateway that automatically created it when I needed it.
IWineGateway _winesGateway;
IWineGateway WinesGateway
{
get
{
if (_winesGateway == null)
_winesGateway = WineGateway.Create();
return _winesGateway;
}
}
This allows me to make calls into the table gateway with one simple line of code, just like I did before using the static methods on CohoDB
protected override WineData OnGetItem(string id)
{
Int32 wineID;
if (!Int32.TryParse(id, out wineID))
{
throw new WebProtocolException(HttpStatusCode.BadRequest);
}
return WinesGateway.GetWine(wineID);
}
Beautiful isn't it? But wait there's more! What about when unit testing? No problem, just inject the Mock factory instead and create a class that implements the gateway interface
[TestInitialize()]
public void MyTestInitialize()
{
// Setup the WineGateway to use the mocks
WineGateway.Factory = new MockWineGatewayFactory();
}
Now my project has both integration and true unit tests. I haven't posted it on the web yet because I've more cleanup work to do and need to finish writing the docs but it's going to be great!
Comments
Anonymous
January 08, 2009
PingBack from http://www.codedstyle.com/mocking-the-tablegateway-pattern-2/Anonymous
January 08, 2009
Factories are great when you have many implementations of the same interface, but when you just need to swap out a dependency for testing it seems like overkill, I prefer the following pattern. <code> public class WineGateway : IWineGateway { IRepository _repository; public void WineGateway(IRepository repository) { _repository = repository; } public void WineGateway() : this(new DefaultRepository()) { } // Implementation... } // normal usage var wineGateway = new WineGateway(); // test usage var wineGateway = new WineGateway(new TestRepository()); // or alternatively using Moq var wineGateway = new WineGateway(CreateMockRepository().Object); </code> I can use the same method to swap out any dependency that WineGateway uses that is not directly related to the unit test I am writing (for example I could also pass an ILog or an INotificationService in the constructor). An added benefit is that it is only a small step to start using Dependency Injection container (IOC). <code> var wineGateway = container.Get<IWineGateway>(); </code>Anonymous
January 09, 2009
Dude - that is brilliant. I'm going to use that method instead. It really simplifies things.