Testing WCF web services

This post is for refining my previous post Test WCF web service – Hosted in ASP.NET Development Server. Personally, I prefer Wordpress because it is easy to maintain layout. But all blogs at wordpress.com are censored by the Great Firewall of China (I always want to say the word that has 4 characters, starts with “F” and ends with “K” when talking about it). So I decided to continue to blog in BlogSpot.

I started working in Microsoft Shanghai Global Technical Support Center on May 5th 2008. Fortunately, the company has a private line connecting to the USA thus we can bypass the Great Firewall in the office. My friends and I have dreamed this all our lives. When I wrote the previous post about testing WCF web service, I just had several weeks of experience in VSTT, so forgive me for not having made it easy to read. Since published that post, I have received many feedbacks, thank you all! It’s time to refine the post.

I remember last year the MSDN document at http://msdn.microsoft.com/en-us/library/ms243399.aspx was the same as that in http://msdn.microsoft.com/en-us/library/ms243399(VS.80).aspx. Then Microsoft has changed the terminology about unit test. It is said that hosting a web service and actually invoking it over HTTP in a test method is considered to be “integration test”, while creating an instance of the web service class in a test method and invoking it’s methods is the “unit test”. So I guess the topic of this post is about how to host WCF web services and perform integration testing.

I focus on testing WCF web services that are hosted in ASP.NET Deployment Server (Cassini).

Background about ASP.NET unit testing.

Let me explain some background about ASP.NET unit testing. ASP.NET unit tests can (not always) be executed in the same process as the web server. See Overview of ASP.NET Unit Tests for more information. At run time of executing ASP.NET unit tests, it goes through the following steps:

  1. Backup your web.config to web.config.backup.
  2. Register an HTTP module Microsoft.VisualStudio.TestTools.HostAdapter.Web.HttpModule, which is in %Program Files%\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\Microsoft.VisualStudio.QualityTools.HostAdapters.ASPNETAdapter. to the web.config.
  3. Start ASP.NET Deployment Server if it is used.
  4. Send a request to the URL pointed by UrlToTest attribute of your test method.
  5. The registered Microsoft.VisualStudio.TestTools.HostAdapter.Web.HttpModule is triggered by the web server. The module subscribes the PageLoad event of the requested page. When PageLoad event is fired, it loads the test assembly in to the web server process and execute the test method.
  6. Restore the web.config and delete web.config.backup after tests complete.

ASP.NET Deployment Server is started when running a unit test if one of the following 2 conditions is meet:

  1. AspNetDeploymentServer attribute is specified.
    This attributes tells VS to start Cassini before executing the test method. Both AspNetDeploymentServer and AspNetDeploymentServerHost expects a physical path to the web application. In a team environment, the web site is usually located in different directories in different computers. The workaround is to use environment variables. See Testing Web Sites and Web Services in a Team Environment for more information. 
  2. UrlToTest and HostType and  AspNetDeploymentServerHost attributes are all specified.
    UrlToTest attribute is required if HostType attribute is set to “ASP.NET”. The combination of the 3 attributes let VS know ASP.NET Deployment Server should be started and the test method should be executed in the same process as the web server.

When testing web services, we usually want to invoke the web services from a proxy and it’s not necessary to execute test methods in the same process as the web server. Therefore, we don’t need to UrlToTest, HostType and AspNetDeploymentServerHost attributes.

Testing WCF web services hosted in ASP.NET Deployment Server

I go through the process of creating a WCF project and add a test method to demonstrate this scenario. Several sentences here are copied from http://msdn.microsoft.com/en-us/library/ms243399(VS.80).aspx.

  1. Create a WCF project in VS via File->New->Project->Visual C#->Web->WCF Service Application.
  2. Generate unit tests against the Web service in the standard way for generating unit tests. For more information, see How to: Generate a Unit Test.
  3. Use svcutil to generate WCF web service proxy.
    1. Right click the Service1.svc in the WCF project in Solution Explorer and select View in Browser.
    2. Run svcutil http://localhost:52747/Service1.svc /config:app.config /out:Service1Proxy.cs /language:C# to generate proxy and config file. Please replace the URL of Service1.svc to that was displayed in the address bar of the browser in #1.
    3. Add Service1Proxy.cs and app.config to the test project.
  4. Remove the attribute HostType, UrlToTest and AspNetDeploymentServerHost for the generated test method.
  5. Add the AspNetDevelopmentServerAttribute attribute to the unit test. The arguments for this attribute class point to the site of the Web service and name the server. For more information, see Ensuring Access to ASP.NET Development Server.
  6. Change the test method to use the generated proxy class to invoke the web service and add the redirection logic. The test class will look like the following.
DownloadIcon sample project
I also paste some code here to ease reverences.

WcfWebServiceHelper.cs

using System;
using System.Reflection;
using System.ServiceModel.Description;
using System.ServiceModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace TestProject1
{
class WcfWebServiceHelper
{
public static bool TryUrlRedirection(object client, TestContext context, string identifier)
{
bool result = true;
try
{
PropertyInfo property = client.GetType().GetProperty("Endpoint");
string webServer = context.Properties[
string.Format("AspNetDevelopmentServer.{0}", identifier)].ToString();
Uri webServerUri = new Uri(webServer);
ServiceEndpoint endpoint = (ServiceEndpoint)property.GetValue(client, null);

EndpointAddressBuilder builder = new EndpointAddressBuilder(endpoint.Address);
builder.Uri = new Uri(
endpoint.Address.Uri.OriginalString.Replace(
endpoint.Address.Uri.Authority, webServerUri.Authority));

endpoint.Address = builder.ToEndpointAddress();

}
catch (Exception e)
{
context.WriteLine(e.Message);
result = false;
}
return result;
}

}
}


Test Method



        [TestMethod()]
[AspNetDevelopmentServer("WcfService1",
"C:\\Users\\Administrator\\Desktop\\TfsRoot\\BuildTest\\Main\\MyServices\\WcfService1")]
public void GetDataTest()
{
Service1Client target = new Service1Client();
Assert.IsTrue(WcfWebServiceHelper.TryUrlRedirection(target,TestContext,"WcfService1"));
int value = 0;
string expected = "You entered: 0";
string actual;
actual = target.GetData(value);
Assert.AreEqual(expected, actual);
}

8 comments:

John Saunders July 14, 2009 5:22 AM

Bill,

Thanks for another great post. People like you should eventually show your government why to at least open the Firewall a crack - you're an asset to the world.

John Saunders

website design,web design July 16, 2009 2:53 AM

Your blog is very nice... i like your blog ....

web design company, web designer, web design India

Anonymous July 31, 2009 1:14 AM

Hi Bill,

Great post! I stumbled into this tool (not free) called WCFStorm (http://www.wcfstorm.com) and during my evaluation of it I found out it did make testing WCF services a lot easier. Plus, it supported netTcpBinding and even custom and user-defined bindings.

Bill Wang July 31, 2009 1:17 AM

Hello

Another option is to use http://www.codeplex.com/WCFLoadTest. That tool can generate unit test from WCF trace log.

steven September 4, 2009 2:34 AM

ITSolusenz departments manage all components ITsolusenz.com of software development including, Application Development Company, software development company india, Software Development Services.

Vania Soft.engr. September 9, 2009 12:00 AM

The development of the IT sector especially in the countries such as India and China has gone a long way in changing the face of these countries. The rise of the middle class, the interest that the foreign market is showing in these countries, and the overall development of the economies of these countries can be massively owed to the growth and flourish of the IT sector. http://www.infysolutions.com/resources/resources.html

James praker October 26, 2009 2:39 AM

There are different web service provider after the Web Designing
because when you live your site you need web services and the way you described here is really great!
Thnx!

riezebosch October 29, 2009 8:54 AM

Repost of my original improvement. I see you also use the EndpointAddressBuilder, but I also introduced generics to prevent casting:

public static bool TryUrlRedirection<T>(ClientBase<T> client, TestContext context, string identifier)
   where T : class
{
   // Retrieving the address on which MSTest is hosting the webservice
   string key = string.Format(CultureInfo.InvariantCulture, "AspNetDevelopmentServer.{0}", identifier);
   Uri webServerUri = context.Properties[key] as Uri;

   // Maybe should throw an exception instead. Returning false is conform the default WebServiceHelper.
   if (webServerUri == null)
   {
      return false;
   }

   // Recreating the endpoint address by replacing the authority of the uri (host and portnumber).
   EndpointAddressBuilder builder = new EndpointAddressBuilder(client.Endpoint.Address);
   builder.Uri = new Uri(builder.Uri.OriginalString.Replace(builder.Uri.Authority, webServerUri.Authority));
   client.Endpoint.Address = builder.ToEndpointAddress();

   return true;
}

Unforntunately there is no way to format my code in this comment :(