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!