Coming from someone who loves the KISS method, I was surprised to find that I was making something entirely too complicated. I know, shocker right? Now I'm no unit testing ninja, and not really a WCF ninja either, but had a desire to test service calls without a) going to a database, or b) making sure that the entire WCF infrastructure was tip top. Who does? It's not the environment I want to test, just the logic I’ve written to ensure there aren't any side effects. So, for the K.I.S.S. method: Assuming that you're using a WCF service library (you are using service libraries correct?), it's really as easy as referencing the service library, then building out some stubs for bunking up data. The service contract We’ll use a very basic service contract, just for getting and updating an entity. I’ve used the default “CompositeType” that is in the template, handy only for examples like this. I’ve added an Id property and overridden ToString and Equals. [ServiceContract]
public interface IMyService
{
[OperationContract]
CompositeType GetCompositeType(int id);
[OperationContract]
CompositeType SaveCompositeType(CompositeType item);
[OperationContract]
CompositeTypeCollection GetAllCompositeTypes();
}
The implementation
When I implement the service, I want to be able to send known data into it so I don’t have to fuss around with database access or the like. To do this, I first have to create an interface for my data access:
public interface IMyServiceDataManager
{
CompositeType GetCompositeType(int id);
CompositeType SaveCompositeType(CompositeType item);
CompositeTypeCollection GetAllCompositeTypes();
}
For the purposes of this we can ignore our implementation of the IMyServiceDataManager interface inside of the service. Pretend it uses LINQ to Entities to map its data, or maybe it goes old school and uses EntLib to talk to SQL. Maybe it talks to a tape spool on a mainframe on the third floor. It really doesn’t matter. That’s the point.
So here’s what our service looks like in its most basic form:
public CompositeType GetCompositeType(int id)
{
//sanity checks
if (id == 0) throw new ArgumentException("id cannot be zero.");
return _dataManager.GetCompositeType(id);
}
public CompositeType SaveCompositeType(CompositeType item)
{
return _dataManager.SaveCompositeType(item);
}
public CompositeTypeCollection GetAllCompositeTypes()
{
return _dataManager.GetAllCompositeTypes();
}
But what about the datamanager? The constructor takes care of that. I don’t want to expose any testing ability in release (or the ability for someone to swap out my datamanager) so this is what we get:
IMyServiceDataManager _dataManager;
public MyService()
{
_dataManager = new MyServiceDataManager();
}
#if DEBUG
public MyService(IMyServiceDataManager dataManager)
{
_dataManager = dataManager;
}
#endif
The Stub
Now it’s time for the rubber to meet the road… Like most guys that ever talk about unit testing here’s a sample that is painting in *very* broad strokes. The important part however is that within the test project, I’ve created a bunk (unit testing purists would say stub I believe) object that implements my IMyServiceDataManager so that I can deal with known data. Here it is:
internal class FakeMyServiceDataManager : IMyServiceDataManager
{
internal FakeMyServiceDataManager()
{
Collection = new CompositeTypeCollection();
Collection.AddRange(new CompositeTypeCollection {
new CompositeType { Id = 1, BoolValue = true, StringValue = "foo 1", },
new CompositeType { Id = 2, BoolValue = false, StringValue = "foo 2", },
new CompositeType { Id = 3, BoolValue = true, StringValue = "foo 3", },
});
}
CompositeTypeCollection Collection { get; set; }
#region IMyServiceDataManager Members
public CompositeType GetCompositeType(int id)
{
if (id <= 0) return null;
return Collection.SingleOrDefault(m => m.Id == id);
}
public CompositeType SaveCompositeType(CompositeType item)
{
var existing = Collection.SingleOrDefault(m => m.Id == item.Id);
if (null != existing)
{
Collection.Remove(existing);
}
if (item.Id == 0)
{
item.Id = Collection.Count > 0 ? Collection.Max(m => m.Id) + 1 : 1;
}
Collection.Add(item);
return item;
}
public CompositeTypeCollection GetAllCompositeTypes()
{
return Collection;
}
#endregion
}
So it’s tough to see in this example why any of this is necessary, but in a real world application you would/should/could be applying much more logic within your service implementation. This all serves to ensure that between refactorings etc, that it doesn’t send sparking cogs all about or let the blue smoke out. Here’s a simple test that brings it all home, remember, broad strokes:
[TestMethod]
public void MyService_GetCompositeType_ExpectedValues()
{
FakeMyServiceDataManager fake = new FakeMyServiceDataManager();
MyService service = new MyService(fake);
CompositeType expected = fake.GetCompositeType(1);
CompositeType actual = service.GetCompositeType(2);
Assert.AreEqual<CompositeType>(expected, actual, "Objects are not equal. Expected: {0}; Actual: {1};", expected, actual);
}
Summary
That’s really all there is to it. You could use software x or framework y to do the exact same thing, but in my case I just didn’t really feel like it. This speaks volumes to my not yet ninja unit testing prowess.