TDD and WCF behavior

Posted by Frederic Hautecoeur on Geeks with Blogs See other posts from Geeks with Blogs or by Frederic Hautecoeur
Published on Sun, 09 May 2010 23:02:54 GMT Indexed on 2010/05/11 2:55 UTC
Read the original article Hit count: 611

Filed under:

Some weeks ago I wanted to develop a WCF behavior using TDD. I have lost some time trying to use mocks. After a while i decided to just use a host and a client. I don’t like this approach but so far I haven’t found a good and fast solution to use Unit Test for testing a WCF behavior.

To Implement my solution I had to :

  • Create a Dummy Service Definition;
  • Create the Dummy Service Implementation;
  • Create a host;
  • Create a client in my test;
  • Create and Add the behavior;

Dummy Service Definition

This is just a simple service, composed of an Interface and a simple implementation.

The structure is aimed to be easily customizable for my future needs.

 

Using Clauses :

   1: using System.Runtime.Serialization; 
   2: using System.ServiceModel; 
   3: using System.ServiceModel.Channels; 

The DataContract:

   1: [DataContract()] 
   2: public class MyMessage 
   3: { 
   4:     [DataMember()]
   5:     public string MessageString; 
   6: } 

The request MessageContract:

   1: [MessageContract()] 
   2: public class RequestMessage 
   3: { 
   4:     [MessageHeader(Name = "MyHeader", Namespace = "http://dummyservice/header", Relay = true)] 
   5:     public string myHeader; 
   6:  
   7:     [MessageBodyMember()] 
   8:     public MyMessage myRequest; 
   9: } 

The response MessageContract:

   1: [MessageContract()] 
   2: public class ResponseMessage 
   3: { 
   4:     [MessageHeader(Name = "MyHeader", Namespace = "http://dummyservice/header", Relay = true)] 
   5:     public string myHeader; 
   6:  
   7:     [MessageBodyMember()] 
   8:     public MyMessage myResponse; 
   9: } 

The ServiceContract:

   1: [ServiceContract(Name="DummyService", Namespace="http://dummyservice",SessionMode=SessionMode.Allowed  )] 
   2: interface IDummyService 
   3: { 
   4:     [OperationContract(Action="Perform", IsOneWay=false, ProtectionLevel=System.Net.Security.ProtectionLevel.None )] 
   5:     ResponseMessage  DoThis(RequestMessage request); 
   6: }

Dummy Service Implementation

   1: public class DummyService:IDummyService 
   2: { 
   3:     #region IDummyService Members 
   4:     public ResponseMessage DoThis(RequestMessage request) 
   5:     { 
   6:         ResponseMessage response = new ResponseMessage(); 
   7:         response.myHeader = "Response"; 
   8:         response.myResponse = new MyMessage(); 
   9:         response.myResponse.MessageString = 
  10:                  string.Format("Header:<{0}> and Request was <{1}>", 
  11:                  request.myHeader, request.myRequest.MessageString); 
  12:         return response; 
  13:     } 
  14:     #endregion
  15:  }

Host Creation

The most simple host implementation using a Named Pipe binding. The GetBinding method will create a binding for the host and can be used to create the same binding for the client.

   1: public static class TestHost
   2: {
   3:     
   4:     internal static string hostUri = "net.pipe://localhost/dummy";
   5:  
   6:     // Create Host method.
   7:     internal static ServiceHost CreateHost()
   8:     {
   9:         ServiceHost host = new ServiceHost(typeof(DummyService));
  10:  
  11:         // Creating Endpoint
  12:         Uri namedPipeAddress = new Uri(hostUri);
  13:         host.AddServiceEndpoint(typeof(IDummyService), GetBinding(), namedPipeAddress);
  14:  
  15:         return host;
  16:     }
  17:  
  18:     // Binding Creation method.
  19:     internal static Binding GetBinding()
  20:     {
  21:         NamedPipeTransportBindingElement namedPipeTransport = new NamedPipeTransportBindingElement();
  22:         TextMessageEncodingBindingElement textEncoding = new TextMessageEncodingBindingElement();
  23:  
  24:         return new CustomBinding(textEncoding, namedPipeTransport);
  25:     }
  26:  
  27:     // Close Method.
  28:     internal static void Close(ServiceHost host)
  29:     {
  30:         if (null != host)
  31:         {
  32:             host.Close();
  33:             host = null;
  34:         }
  35:     }
  36: }

Checking the service

A simple test tool check the plumbing.

   1: [TestMethod] 
   2: public void TestService() 
   3: { 
   4:     using (ServiceHost host = TestHost.CreateHost()) 
   5:     { 
   6:         host.Open(); 
   7:  
   8:         using (ChannelFactory<IDummyService> channel = 
   9:                         new ChannelFactory<IDummyService>(TestHost.GetBinding() 
  10:                             , new EndpointAddress(TestHost.hostUri))) 
  11:         { 
  12:             IDummyService svc = channel.CreateChannel(); 
  13:             try 
  14:             { 
  15:                 RequestMessage request = new RequestMessage(); 
  16:                 request.myHeader = Guid.NewGuid().ToString(); 
  17:                 request.myRequest = new MyMessage(); 
  18:                 request.myRequest.MessageString = "I want some beer."; 
  19:  
  20:                 ResponseMessage response = svc.DoThis(request); 
  21:             } 
  22:             catch (Exception ex) 
  23:             { 
  24:                 Assert.Fail(ex.Message); 
  25:             } 
  26:         } 
  27:         host.Close(); 
  28:     } 
  29: } 

Running the service should show that the client and the host are running fine.

So far so good.

Adding the Behavior

Add a reference to the Behavior project and add the using entry in the test class.

We just need to add the behavior to the service host :

   1: [TestMethod] 
   2: public void TestService() 
   3: { 
   4:     using (ServiceHost host = TestHost.CreateHost()) 
   5:     {
   6:         host.Description.Behaviors.Add(new MyBehavior()); 
   7:         host.Open();¨
   8:

 If you set a breakpoint in your behavior and run the test in debug mode, you will hit the breakpoint.

In this case I used a ServiceBehavior.

To add an Endpoint behavior you have to add it to the endpoints.

   1: host.Description.Endpoints[0].Behaviors.Add(new MyEndpointBehavior())

To add a contract or an operation behavior a custom attribute should work on the service contract definition. I haven’t tried that yet.

 

All the code provided in this blog and in the following files are for sample use.

Improvements

I don’t like to instantiate a client and a service to test my behaviors. But so far I have' not found an easy way to do it.

Today I am passing a type of endpoint to the host creator and it creates the right binding type. This allows me to easily switch between bindings at will.

I have used the same approach to test Mex Endpoints, another post should come later for this.

Enjoy !

© Geeks with Blogs or respective owner