Welcome to EMC Consulting Blogs Sign in | Join | Help

Howard van Rooijen's EMC Consulting Blog (2004 - 2010)

This blog has now moved to http://howard.vanrooijen.co.uk/blog - please update your subscriptions if you wish to receive new content.

Configuring WCF Services for Unit Testing

A couple of weeks ago I pinged Paulo for some help about a WCF service I was writing – he solved the problem I was having and replied that he liked the unit testing pattern I was using for starting the WCF service and couldn't believe he didn't think of it. So I thought I'd blog it, just in case anyone else was interested.

Basically you create a internal ServiceHost wrapper class, which creates a new instance of ServiceHost based on the type of the WCF service you're trying to test; this wrapper class has two static methods exposed: StartService() and StopService() which, starts and stops the ServiceHost instance. If you are using the Visual Studio Unit Testing framework, in your unit test class you call StartService()in the method marked with the [ClassInitialize] attribute and StopService()in the method marked by the [ClassCleanUp] attribute. If you are using MBUnit or NUnit you would use [TestFixtureSetUp] instead of [ClassInitialize] and [TestFixtureTearDown] instead of [ClassCleanUp].

namespace Conchango.WebTwo.Services.Tests
{
   using Microsoft.VisualStudio.TestTools.UnitTesting;
  
using System.ServiceModel;
  
using Conchango.Services.Resourcing;
  
using Conchango.Services.Contracts;
  
using Conchango.WebTwo.CommonTypes.Resourcing;

   [TestClass
   public class ResourcingServiceTests
   {
      /// <summary>
     
/// Start the Resourcing WCF Service so all subsequent
     
/// calls made by the unit test use this connection 
      /// </summary>
     
/// <param name="testContext">Current Test Context</param>
     
[ClassInitialize()]
     
public static void ClassInitialize(TestContext testContext)
     
{
        
ResourcingServiceHost.StartService();
     
}

      /// <summary>
     
/// Shut down the WCF service once all tests have been run
     
/// </summary>
     
[ClassCleanup()]
     
public static void MyClassCleanup()
     
{
        
ResourcingServiceHost.StopService();
     
}

      /// <summary>
     
/// Search for people called "Howard" at Conchango - expect 2 matches
     
/// </summary>
     
[TestMethod]
     
public void PeopleCalledHowardTest()
     
{
        
using (ChannelFactory<IResourcingContract> factory = new ChannelFactory<IResourcingContract>(string.Empty))
        
{
           
IResourcingContract client = factory.CreateChannel();
           
People people = client.GetPeopleByPartialName("Howard");
           
Assert.AreEqual(2, people.Count);
        
}
     
}
   }

   internal class ResourcingServiceHost
  

      internal static ServiceHost Instance = null;
     
     
internal static void StartService()
     
{
        
Instance = new ServiceHost(typeof(ResourcingService));
        
Instance.Open();
     
}

      internal static void StopService()
     
{
        
if (Instance.State != CommunicationState.Closed)
        
{
           
Instance.Close();
        
}
     
}
  
}
}

One other point of note; on the line:

using
(ChannelFactory<IResourcingContract> factory = new ChannelFactory<IResourcingContract>(string.Empty))

the string.Empty is required in the ChannelFactory contructor otherwise the following Exception is thrown: "System.InvalidOperationException: The Address property on ChannelFactory.Endpoint was null. The ChannelFactory's Endpoint must have a valid Address specified."

Published 14 March 2007 09:48 by howard.vanrooijen
Filed under: ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

Console.Write(this.Opinion) said:

Eu sou um leitor compulsivo de blogs e web-sites, mas infelizmente não consigo ler tudo, passo os olhos
March 20, 2007 13:38
 

Frank said:

Troubleshooting WCF Podcast show #94 WCF WCF home Config WCF for unit testing Validating WCF Service

June 18, 2007 00:21
 

ashit patel said:

Thanks for the information. It was really annoying me. But at last i got resolved with above help!!!! Thanks.
November 16, 2007 01:19
 

David said:

Hi Howard,

In this example did you setup your ServiceHost's endpoint(s) via the configuration file?

April 21, 2008 03:32
 

MichaelD! said:

Yes, a full solution outlining this concept would be greatly appreciated, since this is one of the only useful posts that result in "WCF Unit Testing" ...

April 24, 2008 18:53
 

howard.vanrooijen said:

Ok - I'll see what I can do. Thanks for the interest!

April 24, 2008 23:19
 

Ryan said:

I can't seem to get this to work; i assume its because of the config stuff that I have not done correctly. For the life of me I cannot seem to get this to work though.

Any chance of sharing your app.config settings with us?

Thanks

August 18, 2008 10:16
 

samzon said:

very nice article but it would be nice to see a sample application. I see you said you would try to get one together and im just curious as to if youll get around to this.

Thanks.

September 8, 2008 19:57
 

teaspoon said:

your last little bit on the error message helped me solve another problem... thanks!

October 27, 2008 14:23
 

Umit Coskun Aydinoglu said:

Thanx for the article. Dependently, we were writing these piece of code but not as a unittest. Of course it's better to write then as unit test and save time.

December 29, 2008 14:49
 

Graeme said:

Hi

It would be good to see how you set the configuration file in a sample as when I create the service I get configuration errors returned.

March 17, 2009 18:56
 

Surfing_Scotsman said:

I would really like to see the sln as, i cannot get this working ta

March 17, 2009 19:26
 

Alex said:

Instead of writing code to do run the tests, you can also use a tool like WCFStorm (it'll save you a lot of development time) to create the functional test  cases and also execute it.  It has a clean and easy to use GUI for creating the test cases and editing complex data types. You can even create performance test cases.

Check it out here

http://geekswithblogs.net/Erik/archive/2009/04/02/130664.aspx

April 8, 2009 07:51
 

Developerslog.net » Testing WCF services using MSTest said:

April 9, 2009 16:07
 

joe said:

config file please

September 15, 2009 18:14
 

Rick O'Shay said:

A note on namespaces and annotations, they should be leveraged to reduce code.  When testing method Service.SendMessage I call that ServiceTest.SendMessage. The annotation takes care of qualifying it as a test. It's class ServiceTest because you instantiate Service within it, so the Test suffix disambiguates.

Note that Microsoft got lazy and did not leverage context, either. Why isn't the class annotated with [Test] and the method annotated with [Test]? After you 100th time adding TestMethod you sort of scratch your head and mutter, why do I have to add " Method", Don't they know when they are introspecting on a class versus a method?

December 13, 2009 17:47
 

Rick O'Shay said:

On a similar vein, you have StartService and StopService on the ServiceHost class. I didn't think they were starting and stopping a merry-go-round, rather, I assumed they were starting and stopping the service, so why not Start and Stop? I noticed a lot of this sort of style tha reflects poor encapsulation in .NET code in general.

December 13, 2009 17:51
 

Rick O'Shay said:

Why the internal variable Instance caitalized? I'm gettng the idea there aren't actually any standards when it comes t .NET naming conventions. I thought a property method was capitalized, and its bound variable lowercase? This is the death by a thousand knives syndrome: all these tiny inconsistencies combine to create a very foul odor.

December 13, 2009 17:57
 

Luis Borromeo said:

Here is helper class I have written as a facade for service host. Example usage is also provided.

public class WCFHost<TService, TServiceContract, TBinding>

where TBinding : Binding, new()

{

private ServiceHost _serviceHost;

private TBinding _binding;

private EndpointAddress _endpointAddress;

private Uri _baseAddress;

private string _serviceName;

public TBinding Binding

{

get { return _binding; }

}

public EndpointAddress EndpointAddress

{

get { return _endpointAddress; }

}

public Uri BaseAddress

{

get { return _baseAddress; }

}

public string ServiceName

{

get { return _serviceName; }

}

public ServiceHost ServiceHost

{

get { return _serviceHost; }

}

#region constructor

//WCF requires a parameterless constructor to dynamically instantiate a service contract implementation

public WCFHost(string hostBaseAddress, string serviceName)

{

_baseAddress = new Uri(hostBaseAddress);

_serviceName = serviceName;

_endpointAddress = new EndpointAddress(RemoveEndingSlash(hostBaseAddress) + "/" + serviceName);

_serviceHost = new ServiceHost(typeof(TService), BaseAddress);

Initialize();

}

//Singleton Instance overload

public WCFHost(TService singletonInstance, string hostBaseAddress, string serviceName)

{

_baseAddress = new Uri(hostBaseAddress);

_serviceName = serviceName;

_endpointAddress = new EndpointAddress(RemoveEndingSlash(hostBaseAddress) + "/" + serviceName);

_serviceHost = new ServiceHost(singletonInstance, BaseAddress);

Initialize();

}

public void Initialize()

{

CreateServiceEndPoint();

var smb = new ServiceMetadataBehavior();

smb.HttpGetEnabled = true;

_serviceHost.Description.Behaviors.Add(smb);

}

#endregion

// Override if you need to implment different binding logic

public virtual void CreateServiceEndPoint()

{

_binding = new TBinding();

_serviceHost.AddServiceEndpoint(

typeof(TServiceContract),

_binding,

_serviceName);

}

private static string RemoveEndingSlash(string path)

{

char[] charsToTrim = { '/' };

string resultPath;

resultPath = path.EndsWith("/") ? path.TrimEnd(charsToTrim) : path;

return resultPath;

}

}

And here is the example usage.

           //Any binding can be used. For testing, just use basic binding

           private WCFHost<VendorService, IVendorService, BasicHttpBinding> wcfHost;

           //Use basic binding for local testing.

           BasicHttpBinding basicHttpBinding;

           private Uri baseAddress = new Uri("http://localhost:8000");

           private string serviceName = "TestVendorService";

           [TestFixtureSetUp]

           public void SetUpTest()

           {

                 #region Setup/Host the host and add endpoint to service

                 wcfHost = new WCFHost<VendorService, IVendorService, BasicHttpBinding>

("http://localhost:8000", "VendorTest");

                 wcfHost.ServiceHost.Open();

                 #endregion

           }

           [Test]

           public void GetVendorByById()

           {

                 using (var factory = new ChannelFactory<IVendorService>(wcfHost.Binding, wcfHost.EndpointAddress))

                 {

                       IVendorService vendorClient = factory.CreateChannel();

                       var vendorID = UberGuid.ToGuid("DD694F6CBD2D45D8A7B778A34175AA0D");

                       var vendorDetail = vendorClient.GetById(vendorID);

                       Assert.AreEqual(vendorDetail.VENDOR_ID, vendorID);

                 }

           }

           [TestFixtureTearDown]

           public void CleanUp()

           {

                 if (wcfHost.ServiceHost != null)

                 {

                       if (wcfHost.ServiceHost.State != CommunicationState.Closed)

                       {

                             wcfHost.ServiceHost.Close();

                       }

                 }

           }

December 14, 2009 19:44

Leave a Comment

(required) 
(optional)
(required) 
Submit

This Blog

Syndication

News

This blog has now moved - please visit http://howard.vanrooijen.co.uk/blog for new content!
Add to Live.com
Powered by Community Server (Personal Edition), by Telligent Systems