Partager via


Integration Testing WCF Services

In my previous post about unit testing WCF services, I hinted at the need to perform integration testing of services as well. As Jimmy writes, you should still place your logic involving OperationContext in the Service Interface Layer (SIL): In many cases, you need to know something about the context of the operation, such as authentication and authorization, and the SIL is the correct place to place this logic.

Let's, for a moment, consider the need to perform authorization. You could inspect OperationContext.Current directly in each of your operation implementations, but that would be mixing concerns (business logic implemented in the operation mixed together with authorization). The correct way would be to provide a class deriving from ServiceAuthorizationManager, and configure the service to use this class for authorization. This would allow you to keep unit testing your operation implementations, but obviously, you also need to test the authorization manager itself, and it turns out that integration testing is the easiest way to accomplish this task.

To implement an authorization manager, you must override ServiceAuthorizationManager.CheckAccessCore or CheckAccess, which both take an instance of OperationContext as a parameter. If you could initialize a fully populated instance of OperationContext from a test, it would still be possible to unit test a custom authorization manager, but that turns out to be rather complex (OperationContext is a sealed class, so you can't derive a stub from it; on the other hand, its sole constructor takes an instance of IContextChannel, so you could theoretically create a stub of IContextChannel, but that looks like more work than the alternative). For this reason, integration testing is the most efficient approach to testing a custom authorization manager, and indeed all code involving operation context in general (such as message interceptors, etc.).

In the rest of this post, I will show you how easy it is to create a basic integration test of a WCF service. For simplicity's sake, I will start with a basic scenario where I only test the service operation itself. In future posts, I will address more advanced scenarios involving authentication and authorization.

As usual, the example service is stupidly simple:

 public class MyService : IMyService
 {
     #region IMyService Members
  
     public string DoStuff(string msg)
     {
         return msg;
     }
  
     #endregion
 }

The integration tests should follow my principles for integration testing, which in this case are pretty easy to accomplish because WCF allows you to host a service in any managed process. Instead of having to set up a service by deploying it to, say, IIS and configuring the metabase, you can just host it directly in the test project.

Typically, I prefer setting up and starting the service only once per test suite, as starting a service takes a few seconds. As long as the service is stateless, starting the service once and having it handle all the requests from the individual test cases doesn't violate the principle of test case independence, and it saves a lot of time:

 [TestClass]
 public class MyServiceTest
 {
     private static ServiceHost myServiceHost_;
  
     [ClassInitialize]
     public static void InitializeClass(TestContext ctx)
     {
         MyServiceTest.myServiceHost_ =
             new ServiceHost(typeof(MyService));
         MyServiceTest.myServiceHost_.Open();
 

    }

  
     [ClassCleanup]
     public static void CleanupClass()
     {
         MyServiceTest.myServiceHost_.Close();
     }
 }

In this case, I'm using the ClassInitialize and ClassCleanup attributes to start and stop the service corresponding to the lifetime of the test class. I could also have used AssemblyInitialize and AssemblyCleanup to host the same service instance for the entirety of the test library's lifetime, but with services, I could imagine that one would sometimes prefer to be able to test the service with different deployment configurations, and by tying a service instance's lifetime to a test class, one could create different test classes that each host their own service intance.

In the example shown above, the service is hosted using configuration from the application configuration file, so an app.config file containing the necessary configuration data must be added to the test project. Even so, at this point all we have is a running service, but no client.

There are several ways to create a proxy for the service. If you are using contract-first, you can use svcutil.exe to generate a proxy class from your wsdl file. On the other hand, if you rather prefer using the Add Service Reference feature of Visual Studio, you must first host the service, but how do you do that when the service is hosted in the same project where you want to add the proxy?

The first thing to know in this regard is that if you start up a project without debugging, you can still edit the project. Since this is a test project, you need to execute the test list (without debugging). At this point, you probably haven't written any tests yet, but you need at least one (empty) test case to be able to run the test suite. Even so, you will probably not be able to add a service reference using the Visual Studio UI in the very short lifetime of the service instance. A simple hack to solve this problem is to temporarily insert a sleep statement after the service has started:

 [ClassInitialize]
 public static void InitializeClass(TestContext ctx)
 {
     MyServiceTest.myServiceHost_ =
         new ServiceHost(typeof(MyService));
     MyServiceTest.myServiceHost_.Open();
  
     Thread.Sleep(TimeSpan.FromMinutes(1));
 }

This gives you a minute in which you can add the service reference using the Visual Studio UI. When you have done this, remove the Thread.Sleep statement from InitializeClass again. You can now write a test case using the newly created proxy class:

 [TestMethod]
 public void InvokeServiceOperation()
 {
     using (MyServiceClient proxy = new MyServiceClient())
     {
         string response = proxy.DoStuff("Ploeh");
  
         Assert.AreEqual<string>("Ploeh", response);
     }
 }

In a future post, I will describe a more advanced WCF integration testing scenario.

Comments

  • Anonymous
    December 04, 2006
    PingBack from http://blogs.msdn.com/ploeh/archive/2006/12/03/UnitTestingWCFServices.aspx

  • Anonymous
    December 04, 2006
    For in-the-same-project-kinda-tests I often use the ChannelFactory generic which in its simplest form could be used in your example as: Binding b = new WSHttpBinding(); ChannelFactory<IMyService> fact = new ChannelFactory<IMyService>(b,"http://localhost:8080/myservice"); IMyService isrv = fact.CreateChannel(); string a = isrv.DoStuff("test"); Which will of course only work if the config file in your example specify a http binding with the address above. Good post! Look forward to reading the next one.

  • Anonymous
    December 04, 2006
    I forgot to mention that the programmatic approach I showed above has the benefit that you don't need to expose a metadata service endpoint that tools like svcutil.exe will be looking for.

  • Anonymous
    December 04, 2006
    The comment has been removed

  • Anonymous
    December 20, 2006
    In my post about integration testing of WCF services , I hinted that one compelling reason to perform

  • Anonymous
    January 04, 2007
    In my post about integration testing of WCF services , I briefly touched on the topic of authorization

  • Anonymous
    February 06, 2007
    Hi Great article , was looking for some help on using NUnit with WCF. I will just briefly explain my scenario of what WCF is doing and then put across my queries related to NUint testing. My WCF Service does the basic work of performing DB CRUD operation say for example in the code snippet below it does the work of fetching list of employees and adding/updating new employee. The service and operation contract are defined as below [ServiceContract()] public interface IMyService { [OperationContract] EmpStructure[] GetEmployees(); [OperationContract] string AddEmployee(EmpStructure dataContractValue); [OperationContract] string UpdateEmployee(EmpStructure dataContractValue); } My WCF Service class is defined as below and it implements the operation contract GetEmployees(), which returns a list of employees. This is for select operation public class MyService : IMyService { public EmpStructure[] GetEmployees() { EmpList response = new EmpList(); ArrayList emps = new ArrayList(); int i = 0; string EmployeeName=""; SqlConnection cnn = new SqlConnection(ConfigurationSettings.AppSettings["ConnectionString"]); SqlCommand cmd = new SqlCommand("SELECT * FROM emp" , cnn); cnn.Open(); SqlDataReader reader = cmd.ExecuteReader(); while (reader.Read()) { EmpStructure Emp = new EmpStructure(); Emp.EmpId =Convert.ToInt32(reader["EmpId"]); Emp.EmpName =(string)reader["Empname"]; Emp.Basic =(decimal)reader["Basic"]; emps.Add(Emp); i++; } reader.Close(); cnn.Close(); response.Employees = new EmpStructure; i=0; foreach (EmpStructure Employee in emps) { response.Employees = Employee; i++; } return response.Employees ; } } This operation defines Adding of a new employee public string AddEmployee(EmpStructure dataContractValue) { SqlConnection cnn = new SqlConnection(ConfigurationSettings.AppSettings["ConnectionString"]); SqlCommand cmd = new SqlCommand("AddEmp", cnn); cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add(new SqlParameter("@EmpName&quot;, SqlDbType.NVarChar, 50)); cmd.Parameters.Add(new SqlParameter("@Basic&quot;, SqlDbType.Decimal)); cmd.Parameters["@EmpName&quot;].Value =dataContractValue.EmpName; cmd.Parameters["@Basic&quot;].Value = dataContractValue.Basic; cnn.Open(); cmd.ExecuteNonQuery(); cnn.Close(); return "Added Successfully" ; } This operation defines updating an employee information public string UpdateEmployee(EmpStructure dataContractValue) { SqlConnection cnn = new SqlConnection(ConfigurationSettings.AppSettings["ConnectionString"]); SqlCommand cmd = new SqlCommand("UpdateEmp", cnn); cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add(new SqlParameter("@EmpName&quot;, SqlDbType.NVarChar, 50)); cmd.Parameters.Add(new SqlParameter("@Basic&quot;, SqlDbType.Decimal)); cmd.Parameters.Add(new SqlParameter("@EmpID&quot;, SqlDbType.Int )); cmd.Parameters["@EmpName&quot;].Value =dataContractValue.EmpName; cmd.Parameters["@Basic&quot;].Value = dataContractValue.Basic; cmd.Parameters["@EmpId&quot;].Value = dataContractValue.EmpId ; cnn.Open(); cmd.ExecuteNonQuery(); cnn.Close(); return "Updated Successfully" ; } I also have an internal class which is basically used to enummerate through the list of employees and returns a collection of employee. This is represented using the EmpList class [Serializable] public class EmpList { private EmpStructure [] employees; public EmpStructure [] Employees { get { return employees; } set { employees = value; } } } Next I have a DataContract called EmpStructure which represents the values to be passed back n forth the service and which is actually a object representation for the Employee Table. [DataContract] public class EmpStructure { int empId; string empName; decimal basic; [DataMember] public int EmpId { get { return empId;} set { empId = value;} } [DataMember] public string EmpName { get { return empName;} set { empName = value;} } [DataMember] public decimal Basic { get { return basic;} set { basic = value;} } } Questions:

  1. How do I perform unit tests for the above DB related operations in WCF using NUint.
  2. Even if i decouple my service layer and data operation layer (into an class), how would I perform NUnit tests on the class.
  3. I tried creating a NUnit test scenario for the above service proxy, it looks like this code snippet: [TestFixture] public class TestClass { WCFTest.MyServiceClient testClient; WCFTest.EmpStructure[] empObj; [SetUp] public void Init() { testClient = new WCFTestClasses.WCFTest.MyServiceClient(); } [Test] public void getEmp() { empObj = testClient.GetString(); } }
  4. In the above case how will I perform my success and failure tests using Asserts....
  5. Is there any sample available which shows how to perform unit tests for CRUD operations using NUint.
  • Anonymous
    February 06, 2007
    The comment has been removed

  • Anonymous
    March 12, 2007
    Hi, How to use context in Nunit? With MS unit test framework the function with ClassInitialize attribute have TestContext parameter but in NUnit how context can be used? Thanks in advance. Ravi

  • Anonymous
    March 12, 2007
    Hi Ravi Thank you for your question. It's been a couple of years since I used NUnit, but as far as I remember, NUnit doesn't have a test context. To validate, you can ask on an NUnit forum. For a lot of tests, the test context is never used (as is the case in this article). In VSTS, the test context can be used e.g. for data-driven tests, but I don't think NUnit has this concept as well, which is probably the reason why no test context exists - at least, I never had any use for it when I used NUnit, and I still don't need it today with VSTS.

  • Anonymous
    March 12, 2007
    Thanks for your prompt reply :) I need to test methods of WCF service with NUnit. Actually, I have WCF service which contains use of OperationContext.Current from System.ServiceModel to retrieve some data like username. So how can pass the values to set OperationContext.Current from NUnit test method?

  • Anonymous
    March 12, 2007
    Hi Ravi Thank you for clarifying your question. First of all: System.ServiceModel.OperationContext has nothing to do with Microsoft.VisualStudio.TestTools.UnitTesting.TestContext. In your case, that's only a good thing, since NUnit's lack of a test context doesn't stop you from testing services that use OperationContext.Current. It's pretty difficult to override OperationContext.Current from a unit test, but in theory, it should be possible, since this property is a static read/write property. This means that you can create a new instance of OperationContext in the beginning of your test, and then assign it to OperationContext.Current. It's not particularly simple to do, though, since OperationContext is a sealed class, so you can't stub it out. On the other hand, its single public constructor takes an instance of IContextChannel, so if you can stub that out, you should be good to go. However, this is quite a complext interface, so I've never really found it worthwhile to stub it out, but depending on your need, you might be able to get by that fairly easily be using a dynamic mock library. In any case, stubbing out OperationContext.Current is so difficult that I've personally never found it worth the while. That's why I, when I need to test code that accesses OperationContext.Current, find it far easier to write an integration test as outlined in this post. If you want to do it with NUnit, all you need to do is to change the signature of the InitializeClass and CleanupClass methods to fit NUnit's syntax. When you write an integration test as outlined here, WCF will automatically create and populate OperationContext.Current for you. HTH

  • Anonymous
    March 12, 2007
    Hi, Thanks again, Yes, it is difficult to set OperationContext.Current from within the nunit method since we dont have available OperationContext.Current. As u responded that if I write an integration test as outlined here... I wrote intergration test to open service host and it worked fine but OperationContext.Current doesnt get populate. Thanks

  • Anonymous
    March 12, 2007
    Hi Ravi OperationContext.Current should be automatically created by the WCF pipeline before your service method is invoked. On the other hand, it will only be available in the service operations, and not when you, for instance, initialize your service class. I'm not sure if I understand correctly what you are doing. Maybe you could share a simple repro?

  • Anonymous
    March 13, 2007
    I have unit test class library project from where I used a method with[TestFixtureSetUp] attribute to initialize and run the service. In test method I called the service method which has OperationContext.Current to get username. Here I get null value for OperationContext.Current.

  • Anonymous
    March 13, 2007
    Can you share a simple repro of this behavior? I can't readily reproduce it.

  • Anonymous
    March 13, 2007
    Here are unit test methods. [TestFixtureSetUp] public void MyClassInitialize() {  try  {     Console.WriteLine("Class initialized");     serviceHost = new ServiceHost(typeof(MyService));     serviceHost.AddServiceEndpoint(typeof(IMyService), new WSHttpBinding(),   "http://localhost/MyFirstService/MyService");    serviceHost.Open();    }    catch (Exception ex)    {        Console.WriteLine(ex.Message);        Assert.Fail(ex.Message);    }   } [TestMethod]        public void GetUserDetails()        {            try            {                Console.WriteLine("Start calling GetUserDetails function");               MyService myService = new MyService();               object userViewObject = myService.GetUserDetails("ravi");                Assert.IsNotNull(userViewObject, "Returned object can not be null");                Console.WriteLine("End calling GetUserDetails function");            }            catch (Exception ex)            {                throw new Exception(ex.ToString());            }        } Here is part of service method. public UserView GetUserDetails(string userName) {    try    {  string  loginName = null;    foreach (ClaimSet claimSet in OperationContext.Current.ServiceSecurityContext.AuthorizationContext.ClaimSets)    { foreach (Claim claim in claimSet) {    if (claim.ClaimType == CsaClaimType.LoginName)    { loginName = (string)claim.Resource;    }     }    }           }     .     .     .        } OperationContext.Current works fine in service method if it is called from console application and and page when used in it. It it is called from unit test method OperationContext.Current returns null.

  • Anonymous
    March 15, 2007
    Hi Ravi Thank you for sharing your code. However, since this is not a complete implementation, I can't reproduce your problem. Can you share a complete, simple repro of the problem?

  • Anonymous
    January 11, 2008
    Hello, I was wondering is it possible to call the WCF service by adding the dll into the Test Project and maintain the Thread.CurrentPrinciple? Reason i ask is because i am testing a WCF Service and i want to bypass the proxy and call the service dll which has been referenced in my test project. I can successfully start debugging into the WCF service but i get an error when trying to assign the Thread.Principle.Name which is Null. Thanks, Pav

  • Anonymous
    January 11, 2008
    Hi Pav Thanks for writing. You can indeed unit test WCF services, as described in this post: http://blogs.msdn.com/ploeh/archive/2006/12/03/UnitTestingWCFServices.aspx. This effectively bypasses all WCF infrastructure, so you will need to manually populate Thread.CurrentPrincipal as part of your Fixture Setup. I've done that many times, and it works very well.

  • Anonymous
    January 16, 2008
    Hi Ploeh, Thanks for your response Ahh of course in the Test Initialise Setup/Teardown, that makes sense!! I somehow thought the Current Thread was seperate in the two dll's and hence the CurrentPrinciple would not be persisted (duh!) Pav

  • Anonymous
    January 16, 2008
    Hi Pav Good to hear that you were able to address your issue - I'm always happy to be able to help :)

  • Anonymous
    May 16, 2008
    As you may know, testing WCF services is not as simple as referencing a service implementation and start

  • Anonymous
    June 23, 2008
    What is the best way to unit test WCF services when you have callbacks?

  • Anonymous
    June 28, 2008
    Hi Dave Thank you for your question. Since I thought it was of general interest, I wrote a post about it: http://blogs.msdn.com/ploeh/archive/2008/06/28/unit-testing-duplex-wcf-services.aspx HTH

  • Anonymous
    November 11, 2008
    This was an extremely helpful and concise post. Thanks much, Jeremy

  • Anonymous
    January 12, 2009
    ADO.NET Data Services enables you to expose data (including, but not limited to, relational data) as

  • Anonymous
    February 01, 2011
    can any one tel me the sample code for nuit implementation in wcf service