Test-driven development approach for WCF Web services

Introduction

This is the third article of series of articles related to Test-driven development (TDD) approach in Microsoft.NET. My intention is to illustrate this approach with several real-world examples.

In this article, I will show how programmers can use the test-driven development approach to implement and test Web service solutions.

Getting started with the Web service solution

As an illustrative example, we're going to develop a Calculator Web service which is being called from a console application. In order to implement and test the principles of TTD applied to Web service solutions, we're going to create the Web service side as a Console Application project within a solution (see Figure 1).

1.gif

Figure 1

Now we're going to add a class which implements the Calculator WCF Web service (see Figure 2).

2.gif

Figure 2

The first step in the test-driven development approach is, of course as its name implies, the formulation of a list of tests. One important thing to keep in mind is that the list of tests is dynamic in order to add or remove tests for testing in different environments.

In this case, the list test case for the Calculator Web service (as illustrative example, just one method to add two integers) is very simple, as follows:

  • Create an instance of a proxy for the Web service and send two integers to be calculated. We're testing that the communication is established and the underlying business logic of the Web service is correct.

In order to implement these test cases, we have to define the interface between the Calculator Web services, the proxy and the test cases. One way that I follow is to specify the interface of the Calculator service and then we reference the interface and call the methods using the proxy for the service.

I like a lot this approach because when we define interfaces for the classes, conceptually we're separating the abstraction layer from the implementation. So, our class instances provide its interfaces as roles and communication mechanisms (independent of the implementation) to external entities which want to communicate to and consume the services. If later we want to change the implementation of the service (to upgrade the services or for possible errors discovered in the test phase); then we make the changes in an agile way without changing the interface and breaking the line with the consumers of the interface services. From the point of view of technology is an advantage too, because, the clients which use our Web service don't need to re-build or change some code due to the interface is stable.

Let's define the ICalculator interface as well as the Calculator class representing the Web service (see Listing 1).

using System;

using System.ServiceModel;

 

namespace TDDWebServiceApp

{

    [ServiceContract()]

    public interface ICalculatorService

    {

        [OperationContract]

        int Add(int nParam1, int nParam2);

    }

 

    public class CalculatorService : ICalculatorService

    {

        public int Add(int nParam1, int nParam2)

        {

            throw new Exception("The method or operation is not implemented.");

        }

    }

}

Listing 1

Now let's specify the configuration file with the parameters necessary to run the Web service in its hosting environment (see Listing 2).

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

          <system.serviceModel>

                   <services>

                             <service name="TDDWebServiceApp.CalculatorService" behaviorConfiguration="returnFaults">

                                      <endpoint contract="TDDWebServiceApp.ICalculatorService" binding="wsHttpBinding"/>

                             </service>

                   </services>

                   <behaviors>

                             <serviceBehaviors>

                                      <behavior name="returnFaults" >

                                                <serviceDebug includeExceptionDetailInFaults="true" />

                                                <serviceMetadata httpGetEnabled="true" />

                                      </behavior>

                             </serviceBehaviors>

                   </behaviors>

          </system.serviceModel>

</configuration>

Listing 2

And the code to create the hosting environment which manages the life cycle of the Calculator Web service (see Listing 3).

using System;

using System.Collections.Generic;

using System.Text;

using System.ServiceModel;

 

namespace TDDWebServiceApp

{

    public class Program

    {

        ServiceHost m_objServiceHost = null;

 

        public void StartService()

        {

            Uri baseAddress = new Uri("http://localhost:8888/TDDWebServiceApp/CalculatorService");

            this.m_objServiceHost = new ServiceHost(typeof(CalculatorService), baseAddress);

            this.m_objServiceHost.Open();

        }

 

        public void StopService()

        {

            if (this.m_objServiceHost.State != CommunicationState.Closed)

                this.m_objServiceHost.Close();

        }

 

        static void Main(string[] args)

        {

            Program objProgram = new Program();

            System.Console.WriteLine("Starting the service CalculatorService");

            objProgram.StartService();

            System.Console.WriteLine("Press any key to finish the service ...");

            System.Console.ReadKey();

            objProgram.StopService();

        }

    }

}

Listing 3

Now you can compile and run the Web service.

The first step to test the Calculator Web service is to create the proxy for the service. Let's create Class Library project to include the proxy and the test suite as shown in Figure 3.

3.gif

Figure 3

In order to create the proxy for the service, you should run the console hosting the service and move to the directory path of the test suite class library and runt he following command (see Listing 4) which automatically creates a config file and a file with the logic of proxy to communicate correctly with the server.

svcutil http://localhost:8888/TDDWebServiceApp/CalculatorService?wsdl

Listing 4

Then, include the generated file in the project add a reference to the System.ServiceModel assembly. You have to add a reference to the NUnit framework (see Figure 4) and a fixture class (see Figure 5).

4.gif

Figure 4

5.gif

Figure 5

Now it's time to write code for the test cases using the test method TestCalculator annotated with Test attribute. The test to check if you add two integers and result is correct. It's remarkable to say that these methods must be declared as public, be an instance method (non-static), with return type as void, and take no parameters (see Listing 6).

using System;

using System.Collections.Generic;

using System.Text;

using NUnit.Framework;

 

namespace CalculatorSvcTestSuitePkg

{

    [TestFixture]

    public class CalculatorServiceFixture

    {

        [Test]

        public void TestCalculator()

        {

            CalculatorServiceClient objClient = new CalculatorServiceClient();

            Assert.AreEqual(3, objClient.Add(1,2));

        }

    }

}

Listing 5

Now let's test our business logic and the communication with the CalculatorService Web service. Right-click on the CalculatorSvcTestSuitePkg project and select Properties from the context menu. In the Debug tab, set GUI NUnit test runner (see Figure 6).

6.gif

Figure 6

Now let's build the solution and run the test. When the GUI NUnit test runner is run for the first time, we need to load the test project (see Figure 7).

7.gif

Figure 7

If you run the test you receive an exception because we have not implemented the service (see Figure 8).

8.gif

Figure 8

Now let's go to the service and implement the business logic to later test the service (see Listing 6).

using System;

using System.ServiceModel;

 

namespace TDDWebServiceApp

{

    [ServiceContract()]

    public interface ICalculatorService

    {

        [OperationContract]

        int Add(int nParam1, int nParam2);

    }

 

    public class CalculatorService : ICalculatorService

    {

        public int Add(int nParam1, int nParam2)

        {

            return nParam1 + nParam2;

        }

    }

}

Listing 6

Now if you launch the GUI test runner again, you can see that the test cases passed (see Figure 9).

9.gif

Figure 9

Conclusion

In this article, I've illustrated how programmers can use the test-driven development approach to implement and test a Web service. Now you can apply this approach to your own business solutions.


Similar Articles