posts - 76, comments - 26, trackbacks - 0

Testable WCF ServiceHost

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:

  1. 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.
  2. It’s Heavy and Hard to Setup – IIS is required to be installed on any machine that wants to run the tests.
  3. 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.

Print | posted on Tuesday, March 17, 2009 11:50 PM | Filed Under [ .net development testability wcf ]

Feedback

Gravatar

# re: Testable WCF ServiceHost

Hmmm.. gaps in interoperability, complex, vast, but not testable. When you essentially have no resource limits on software projects (for all intents and purposes Microsoft didn't) you can throw armies of warm bodied testers and fixers at problems. It's frightening that Microsof only recently came around to unit testing (cost savings and increased quality being the primary benefits). Ditto for n-tier design, still a no-show in terms of dependency injection. Scary, sad, dissappointing.

You see it TO THIS DAY. The online demos start with "we'll skip adding the test project to make it simple" instead of using that as the demo of how to build whatever it is they are showing. You see them enable meta data so they can gen-up a an application to drive a WCF example rather than a unit test. They still don't get it. Happily, they are going in the right direction! They have even turned on Flashlight (or is it SilverLIght) to the importance of n-tier and unit testing.
12/15/2009 9:02 AM | Rick O'Shay

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 4 and 7 and type the answer here:

Powered by: