I’m finally getting a chance to sink my teeth into Windows Communication Foundation (WCF). I’m finding quite a few gaps on the interoperability side of things, and I think it’s overly complex in it’s configuration schemes and extensibility points, but it’s light years beyond what we had previously in ASMX. From what I can tell, the WCF adoption was just much slower than everyone anticipated. I’m not sure why that is – perhaps it’s the vastness of it that’s intimidated people. Perhaps it was the quality of the the .NET version 2.0 release that resulted in a slow adoption of all of version 3.0+. For whatever reason, this seems to have caused a number of rough edges in the product to sit dormant for a couple of years now.
At any rate, WCF is now part of my every day day-job, and I’m trying to find ways to make it more testable. We have a suite of what I’d call System Tests. Unit Tests by my definition, test at the object (or component) boundary while System Tests test at a collection of objects (or components). Our system is highly configurable, and behaviors vary dramatically from one configuration to the next. We’d like to have the ability to have a repeatable suite of automated test that can test all of our out-of-the-box configurations.
To date we’ve been executing our tests by .bat file (shelling to MbUnit.Cons.exe) after copying copies of our config files to our hosting IIS project. This is less than awesome for a few reasons:
- It’s Slow and Flakey – Running test out of process (which is what IIS is) is always slower than in-process. Keep our test suite blazing fast is highly desirable. Also, when you’re doing this kind of thing, it’s not uncommon to need to restart IIS between tests (IIS caching certain types of config data, etc.), which can really slow down the party.
- It’s Heavy and Hard to Setup – IIS is required to be installed on any machine that wants to run the tests.
- It’s Awkward – We must have .bat files that copy around our Web.config files that contain our test configuration before executing our tests. The natural experience of running our tests directly from the IDE is lost.
Luckily, WCF is highly extensible and customizable in it’s hosting infrastructure. In the System.ServiceModel of the .NET Framework, they expose everything you need to easily create your own host for your WCF services. We’ve all seen the MSN samples that hosts your hello-world service with a Console app. To make our automated tests more awesome, I created a TestableServer service host implementation.
The Service
Lets say we have a service like this…
/// <summary>
/// Here's my service implementation
/// </summary>
public class SomeService : ISomeService
{
public DateTime GetCurrentDate()
{
return DateTime.Now.Date;
}
}
/// <summary>
/// Here's my service contract for my implementation
/// </summary>
[ServiceContract]
public interface ISomeService
{
[OperationContract]
DateTime GetCurrentDate();
}
The Test
With the TestableServer, I can write a tidy little test like so…
[TestFixture]
public class SomeServiceFixture
{
[Test]
public void Can_We_Invoke_GetCurrentDate()
{
using (TestableServer<SomeService, ISomeService> server = new TestableServer<SomeService, ISomeService>(new BasicHttpBinding()))
{
ISomeService proxy = server.CreateProxy();
DateTime actual = proxy.GetCurrentDate();
Assert.AreEqual(DateTime.Now.Date, actual.Date);
}
}
}
By passing a few generics, it has all the information it needs to host my service in-memory and I can execute tests against it directly. Notice in the TestableServer constructor, I’m passing in the binding I’d like to use. Here I can specify netTcpBinding, msmqBinding, or any of they other support bindings. Here I’m using vanilla HTTP.
The Code
There’s not much to it at all, but here’s the code behind TestableServer…
public class TestableServer<TServiceImp, TServiceInt> : IDisposable where TServiceImp : class
{
private ServiceHost _host;
private readonly Type _serviceImplType = typeof(TServiceImp);
private readonly Type _serviceIntType = typeof(TServiceInt);
private readonly string _url;
private readonly Binding _binding;
private ChannelFactory<TServiceInt> _factory;
private TServiceInt _channel;
public TestableServer() : this(new NetTcpBinding()) { }
public TestableServer(Binding binding)
{
_host = new ServiceHost(_serviceImplType);
_binding = binding;
_url = string.Format("{0}://localhost/{1}", _binding.Scheme, Guid.NewGuid());
_host.AddServiceEndpoint(_serviceIntType, _binding, _url);
try
{
_host.Open();
}
catch (TimeoutException timeoutEx)
{
Console.WriteLine("Failed to close the service host - Exception: " + timeoutEx.Message);
throw;
}
catch (CommunicationException communicationEx)
{
Console.WriteLine("Failed to close the service host - Exception: " + communicationEx.Message);
throw;
}
}
public TServiceInt CreateProxy()
{
_factory = new ChannelFactory<TServiceInt>(_binding);
_channel = _factory.CreateChannel(new EndpointAddress(_url));
return _channel;
}
public void Dispose()
{
if ( _factory != null )
{
_factory.Close();
_factory = null;
}
TearDown();
}
private void TearDown()
{
if (_host != null)
{
try
{
if ( _host.State == CommunicationState.Opened || _host.State ==CommunicationState.Opening)
{
_host.Close();
}
}
catch (TimeoutException timeoutEx)
{
Console.WriteLine("Failed to close the service host - Exception: " + timeoutEx.Message);
}
catch (CommunicationException communicationEx)
{
Console.WriteLine("Failed to close the service host - Exception: " + communicationEx.Message);
}
finally
{
_host = null;
}
}
}
}
The Ultimate Goal
So, I haven’t actually achieved my ultimate goal yet, which is to be able to programmatically configure the server/service for each of our supported configurations and execute our test suites against those configurations. That’s what I’m going to work on next, and without IIS between my test fixture and my service implementation, achieving that goal should be relatively simple.