Testing WCF Service using Visual Studio 2008 Unit Tests

28 בנובמבר 2008

תגובה אחת

Testing Windows Communication Foundation (WCF) service should be handled in a slightly different way than other "regular" .net types. We should test the service logic (as always) but we also need to test it in few more manners to assure our service quality:

  • Test the service within WCF runtime environment – which can raise several issues that would not be in regular CLR environment.
  • Test for your default binding and for all other bindings required from the service specs.
  • Test DataContract serialization – you will be surprise by a service failure when you see the service is running ok but client still get error (empasizes with REST service)

* The method I will show here uses Visual Studio 2008 Unit Tests framework but it can easily be ported into NUnit or other framework.

Tests Inheritance

Any Unit Test is a plain .net class, so we can use inheritance, polymorphism and overriding to re-use the original (logic) test of the service. The driven unit test class should contain the "TestClass" attribute at least, all methods on parent class marked with "TestMethod" attributes will run also when testing the concrete class.

Let's start

First, we will define the target of the test, the obvious Calculator Service (I always use interface for ServiceContract, also in Demo :)):

   1:  // Defines the Calculator Service contract
   2:  [ServiceContract]
   3:  public interface ICalculatorService
   4:  {
   5:      [OperationContract]
   6:      int Add(int a, int b);
   7:   
   8:      [OperationContract]
   9:      int Subtract(int a, int b);
  10:   
  11:      [OperationContract]
  12:      int Multiply(int a, int b);
  13:   
  14:      [OperationContract]
  15:      int Divide(int a , int b);
  16:  }
  17:      

 

Then, you should define your service implementation (I'll skip it, you can download the full code here). We will test the contract interface and not the concrete implementation be able to test another implementations of the ICalculatorService contract. The logic-test will be defined (code trimmed):

   1:  [TestClass]
   2:  public class CalculatorTest
   3:  {
   4:      ...
   5:      protected virtual ICalculatorService CreateTarget()
   6:      {
   7:              ICalculatorService target = new CalculatorService();
   8:              return target;
   9:      }
  10:   
  11:      [TestMethod]
  12:      public void Add2Positive()
  13:      {
  14:          int a = 1;
  15:          int b = 2;
  16:          int expected = 3;
  17:   
  18:          ICalculatorService calculator = CreateTarget();
  19:          int actual = calculator.Add(a,b);
  20:   
  21:          Assert.AreEqual(expected, actual);
  22:      }
  23:      ...
  24:  }

the key method here is the CreateTarget which creates the target of the test and it is marked as protected virtual so we can override it in sub-class and the test will keep its logic. And we will do just that:

Testing the service within its real environment – WCF runtime

   1:      [TestClass]
   2:      public class CalculatorServiceTestAsWCFService 
   3:                            : ICalculatorServiceTest
   4:      {
   5:          private ServiceHost m_serviceHost;
   6:          [TestInitialize]
   7:          public void InitializeHost()
   8:          {
   9:              m_serviceHost = new ServiceHost(
  10:                          typeof(CalculatorService));
  11:              m_serviceHost.AddServiceEndpoint(
  12:                          typeof(ICalculatorService), 
  13:                          GetTestBinding(), 
  14:                          new Uri( GetTestAddress()));
  15:   
  16:              m_serviceHost.Open();
  17:          }
  18:   
  19:          protected virtual string GetTestAddress()
  20:          {
  21:              return "http://localhost:9999/Calculator";
  22:          }
  23:   
  24:          [TestCleanup()]
  25:          public void CloseHost()
  26:          {
  27:              if (m_serviceHost != null)
  28:              {
  29:                  m_serviceHost.Close();
  30:              }
  31:          }
  32:   
  33:          protected virtual Binding GetTestBinding()
  34:          {
  35:              return new WSHttpBinding();
  36:          }
  37:   
  38:          protected override ICalculatorService CreateTarget()
  39:          {
  40:              ICalculatorService calcServiceClient = 
  41:                  ChannelFactory<ICalculatorService>.CreateChannel(
  42:                                      GetTestBinding(), 
  43:                                      new EndpointAddress(GetTestAddress()));
  44:   
  45:              return calcServiceClient;
  46:          }
  47:      }

Here we inherits from our logic-test class and add several methods:

  1. InitializeHost – on each test, we create a host, and add test service endpoint using the ICalculatorService contract, test address and binding
  2. GetTestAddress,GetTestBinding – virtual methods for generating the testing address and binding. If you want to test your service against several bindings you just need to inherit this class and override these 2 methods.
  3. CloseHost – on test cleanup, closes the host.
  4. CreateTarget – overrides the base class' method to provide a real WCF client for the test, so the test methods will now work in environment as close as possible to the real environment (including serializations, interceptions etc.)

Notes

  • This method assume that the target service logic is not depend on WCF runtime environment like OperationContext for example.
  • Don't over-test the WCF, meaning, test only bindings you want to guaranty (if you work with Compact Framework clients, it would be basicHttpBinding, for example) 

Links

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *

תגובה אחת

  1. Amol Gholap8 ביוני 2009 ב 5:38

    Simple and sweet, like it!

    הגב