TDD a WCF service, is it possible?

Right now I am working on a CQRS project, and I am using WCF as my web service technology. In this project I decided to challenge myself and make a full TDD project. The first big problem I found was to simulate a real web server, because I didn’t want to hit a production or QA environment to run my tests.
Second I didn’t want to write any integration test at all, so I had to figure out how to create an in-Process test to run my WCF methods.

Why in-process test? When you write a unit test, you should test a single functionality, and in order to make the test atomic, it should run in-process. It should not involve third party tiers like a database or IIS, otherwise the test won’t be in-process anymore, so it will became an Integration test.

The first idea I came out was to fake the behaviour of IIS, and in order to accomplish this I could easily work with a mock framework like Typemock and mock out the entire IIS infrastructure.

Is this what I really want? Not at all. I don’t want and I don’t need to fake the behaviour of IIS, I need an instance of it because my WCF services are using custom factories and custom behaviours and I need to be sure they are acting in the right way.

So I have created two different type of tests. The first one is pure Unit Test, where I verify the command behaviours:

   1: [TestMethod]

   2: public void AddUsersCommandHandler_ValidCommand_WillPersist()

   3: {

   4:     // arrange

   5:     AddUsersCommand expectedCommand = new AddUsersCommand { UsersStream = expectedStream };

   6:     // service with mocked unit of work

   7:     IServiceCommandHandler<AddUsersCommand> handler = new AddUsersCommandHandler(mockUnit.Object);

   8:  

   9:     // act

  10:     handler.Handle(expectedCommand);

  11:  

  12:     // assertions against the command behaviour

  13:     mockUnit

  14:         .Verify(x => x.BeginTransaction(), Times.Once());

  15:     mockUnit

  16:         .Verify(x => x.Save(It.IsAny<User>()));

  17:     mockUnit

  18:         .Verify(x => x.CommitTransaction(), Times.Once());

  19: }

The second one is the (in)famous integration test, where you call the WCF service and test the overall process. Unfortunately for the second step I couldn’t find anything really TDD so I had to figure out by myself a different solution.

IIS Express

Yesterday I spent some time googling around for a solution while I found a nice post from Scott Gu about IIS express. With IIS express we have an in-process IIS server that we can startup or shutdown at any time during the lifecycle of our tests. It provides almost all the functionalities provided by IIS and it also trigger the Application_Start event, something not very well handled by Cassini …

So, first of all I had to figure out how to bootstrap my unit tests to run over a different IIS than the one configured in the web.config.

Piece of cake, first of all I need something that allows me to bootstrap IIS express before my unit test harness starts:

   1: [ClassInitialize]

   2: public static void ClassInitialize(TestContext context)

   3: {

   4:     // create a process for IIS express

   5:     issProcess = new Thread(IssUtility.StartIisExpress) 

   6:                  { 

   7:                     IsBackground = true 

   8:                  };

   9:     // start it

  10:     issProcess.Start();

  11:     // create a new channel factory using the generated URL

  12:     factory = new ChannelFactory<IDomainWriteService>(

  13:        new BasicHttpBinding(), 

  14:        new EndpointAddress("http://localhost:5555/services/DomainWriteService.svc"));

  15: }

Then I need another utility to kill everything when my test harness is done:

   1: [ClassCleanup]

   2: public static void ClassCleanup()

   3: {

   4:     // close the WCF factory

   5:     factory.Close();

   6:     // shutdown IIS express

   7:     IssUtility.TearDownIis();

   8:     issProcess.Abort();

   9: }

At this point I can write any test in the following format:

   1: [TestMethod]

   2: public void ChannelFactory_ReceiveValidCommand_WillExecute()

   3: {

   4:     var expectedCommand = BuildAddUsersCommand();

   5:     service.AddUsers(expectedCommand);

   6: }

I found this technique very useful because we work with TFS on Azure so my Build environment cannot be polluted at all with any third party components or by installing the WCF service under test in the build machine.

In the same way I found very quick to bootstrap IIS express for my tests, honestly they are faster than running on my local IIS 8.

IIS Express utility class

Below is the code I use to initialize IIS express, feel free to use it in your tests and let me know if you come up with a better solution than this one.

   1: namespace xxx

   2: {

   3:     using System.Diagnostics;

   4:  

   5:     public class IssUtility

   6:     {

   7:         private static Process iisProcess;

   8:         

   9:         // Start IIS on port 5555

  10:         public static void StartIisExpress()

  11:         {

  12:             var startInfo = new ProcessStartInfo

  13:                 {

  14:                     WindowStyle = ProcessWindowStyle.Normal,

  15:                     ErrorDialog = true,

  16:                     LoadUserProfile = true,

  17:                     CreateNoWindow = false,

  18:                     UseShellExecute = false,

  19:                     Arguments =

  20:                         string.Format("/path:\"{0}\" /port:{1}",

  21:                                         @"your WCF dev path", 5555)

  22:                 };

  23:  

  24:             var programfiles = string.IsNullOrEmpty(startInfo.EnvironmentVariables["programfiles"])

  25:                                     ? startInfo.EnvironmentVariables["programfiles(x86)"]

  26:                                     : startInfo.EnvironmentVariables["programfiles"];

  27:  

  28:             startInfo.FileName = programfiles + "\\IIS Express\\iisexpress.exe";

  29:  

  30:             try

  31:             {

  32:                 iisProcess = new Process {StartInfo = startInfo};

  33:  

  34:                 iisProcess.Start();

  35:                 iisProcess.WaitForExit();

  36:             }

  37:             catch

  38:             {

  39:                 iisProcess.CloseMainWindow();

  40:                 iisProcess.Dispose();

  41:             }

  42:         }

  43:  

  44:         // kill everything

  45:         public static void TearDownIis()

  46:         {

  47:             iisProcess.CloseMainWindow();

  48:             iisProcess.Dispose();

  49:         }

  50:     }

  51: }

Enjoy!