TDD: Test driving the Vecozo webservice

This is not my usual kind of subject. Most of my post are on the diversity of share worthy tidbits I encounter while building apps. Test Driven Design is quite a hot topic here on Codebetter. So far I had nothing to add to the great writings of Jeremy et al. But a discussion on a post by Jay on Javascript struck me. It gave the impression TDD as something too big or complicated to get started with. In this post I will describe some of my experiences with TDD. It’s a very simple story on a very simple problem but it makes clear how TDD is also for me an easy way to tackle problems.

This story deals with the Vecozo web service. This secured web service offers a way to check Dutch medical insurance data. The number of pages in the documentation is gigantic but there is no clear example, not even a hint, how to use the service from code. I will use TDD to explore the service and will end with the base for a simple API.

The Vecozo package contains:

  • An url to (a test location of) the web service
  • A certificate (obtainable on request)
  • A huge document describing the inner decision paths and return codes of the web service
  • A description of the request and the response messages
  • Spreadsheets describing available test data
  • The remarks that the service has been built using .NET 1.1 and WSE

What is lacking is the way to combine all of this to make successful requests.

This is where TDD comes in. I am using nunit the mother of all unit test frameworks. My tests are located in a new class library project added to my solution. The test project references the nunit framework and the application project. In the tests I can call all public members of the application. The test code has a reference to the application I’m building but the application itself does not reference anything extra; I do not force my customer into automated testing.

There are several tools available to actually run the tests, both command line and GUI versions. Resharper has a nice testrunner which integrates into Visual Studio. As an alternative Nunit contains one which looks and behaves just the same.

The test have to be public classes of the new lib. The test runner instantiates objects of Classes decorated with the TextFixture attribute. Public methods in the classes, decorated with the Test attribute, contain the test code to be executed.

What code the test will execute depends. A way to start could be to take existing code and automatically test metrics to check the behavior of that code. A test passes when it runs without throwing an exception. To harden a test you can conditionally raise (assertion) exceptions, the nunit Assert class has loads of usable members to do that. Designing and coding these kinds of test is not always easy. What metrics can/should you test ? What algorithm you need to assert that the test passes ?

The methodology of Test Driven Development approaches the matter the other way round. The first line of code is the test itself. The implementation of the application is code built to pass those tests. Quite an eye-opener on where to start is Jimmy Nilson’s book on Domain Driven Design I read last summer. At first read it chatters away, writing almost trivial code. In tests. But building test upon test a quite good application emerges.

Let’s give this a try on the Vecozo web service. I start writing out questions in code on what I want to build.

The first question:

  • Can I create an object to wrap up handling the web service?.

Written out as test

[TestFixture]

public class VecozoWebServiceTests

{

   [Test]

   public void CanCreateWrapper()

   {

      VecozoLib.ServiceWrapper o = null;

      Assert.IsNotNull(o);

   }

}

For the test code to build I need to write the ServiceWrapper class.

namespace VecozoLib

{

   public class ServiceWrapper

   {

   }

}

The code builds but the test fails. For it to pass the wrapper object has to be properly instantiated.

[Test]

public void CanCreateWrapper()

{

   VecozoLib.ServiceWrapper;

   o = new ServiceWrapper();

   Assert.IsNotNull(o);

}

This test may seem quite trivial. But it does assert that a ServiceWrapper object can be instantiated, it’s constructor did not raise an exception. When refactoring the test will become even more valuable. When the constructor is refactored into a factory, the (slightly rewritten) test will assert the factory does create objects.

Here’s the unit testing mantra: Red, Green, Refactor. Write a test which fails, write the code to make it pass and safely refactor the code to the evolving needs. For the latter the tests will guard the code to keep working

Now the first test reads green and we have a starting place. The object is going to invoke the web service. I do have an url for that so I can add a web reference. VS generates a proxy to the web service. The service appears to have one web method which does all the work. The next question is:

  • Can I invoke this webmethod ?

I don’t care yet whether I’m invoking it correctly, I just want to see whether the web service accepts my requests and returns something. The simplest request sends an empty list to validate. This is implemented in a new method of the service wrapper.

public class ServiceWrapper

{

   public object InvokeVecozoService()

   {

      vz3738 ci = new vz3738();

      Console.WriteLine(ci.Url);

      ControleerInput requestMessage = new ControleerInput();

      requestMessage.Verzekerden = new AanvraagCovType[0];

      object responseMessage = ci.controleer(requestMessage);

      return responseMessage;

   }

}

Vz3738 is the webservice, the webmethod is named Controleer. Note that the method writes the url of the service to the console. All the test does is call this method.

[Test]

public void CanInvokeService()

{

   VecozoLib.ServiceWrapper sw = new ServiceWrapper();

   object response = sw.InvokeVecozoService();

   Assert.IsNotNull(response);

}

The test fails on an exception.

The nice thing is that the test runner catches the output. The url is there and also a stack trace. The message is loud and clear : no Access (Geen toegang). It’s time to do something with my Vecozo certificate.

Next question:

  • Can I get a (non expired) certificate

I want the serviceWrapper to expose the Vecozo certificate. Written out in a test

[Test()]

public void ValidCertificateAvaliable()

{

   VecozoLib.ServiceWrapper sw = new ServiceWrapper();

   System.Security.Cryptography.X509Certificates.X509Certificate cert = sw.VecozoCertificate;

   Assert.IsTrue(DateTime.Parse(cert.GetExpirationDateString()) > DateTime.Now);

}

Unless a valid Vecozo certificate is installed the test will fail. The implementation of the code to get the certificate is pretty straightforward.

public X509Certificate VecozoCertificate

{

   get

   {

      X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);

      certStore.Open(OpenFlags.ReadOnly);

      X509Certificate2Collection certs = certStore.Certificates.Find(X509FindType.FindByIssuerName, “Vecozo”, false);

      if (certs.Count == 1)

         return certs[0];

      else

         return null;

   }

}

Now the test shows I do I have a certificate. But what to do with it? Sign the message? Encrypt it ? There are a lot of possibilities. The Vecozo docs are blank on this. Googling around I found the simplest thing to do with a certificate is just pass it to the service in the ChildCertificates collection of the proxy. Let’s try this.

public object InvokeVecozoService()

{

   vz3738 ci = new vz3738();

   Console.WriteLine(ci.Url);

   ci.ClientCertificates.Add(VecozoCertificate);

   ControleerInput requestMessage = new ControleerInput();

   requestMessage.Verzekerden = new AanvraagCovType[0];

   object responseMessage = ci.controleer(requestMessage);

   return responseMessage;

}

This simplest thing is exactly what this service demands. When the certification test passes now the connection test also passes. And now I start communicating with the service. The unit test are a great status monitor; they test the status of the certificate and the availability of the service in a single click.

Now my tests can jump right into testing the domain specific features of the service itself. The Assert class of the nUnit framework has loads and loads of (static) methods to compare expected values with the actual values returned from the service.

There are many ways to request data from the Vecozo web service. The documentation does give a good overview. In the next example I am requesting the data of one person based on birth date and BSN (you can compare that to a SSN). In case you are going to work with the actual web service check the documentation on this. In case you are just interested in TDD it suffices to say that the method returns some domain specific data. Note that the actual invocation of the web service has already been refactored into a (private) method. The earlier test will make sure I have done that correctly.

public RetourinfoCovType PersonData(DateTime birthDate, int bsnNummer)

{

   ControleerInput requestMessage = new ControleerInput();

   requestMessage.Verzekerden = new AanvraagCovType[1];

   requestMessage.Verzekerden[0] = new AanvraagCovType();

   requestMessage.Verzekerden[0].PeildatumVerzekering = DateTime.Now;

   requestMessage.Verzekerden[0].SoortVerzekering = 92;

   requestMessage.Verzekerden[0].GeboorteDatum = birthDate;

   requestMessage.Verzekerden[0].BSN = bsnNummer;

   requestMessage.Verzekerden[0].BSNSpecified = true;

   return getResponseMessage(requestMessage)[0];

}

A simple straightforward test against the test data available to check the domain specific functionality of the code:

[Test]

public void IsDeceased()

{

   VecozoLib.ServiceWrapper sw = new ServiceWrapper();

   VecozoLib.VecozoService.RetourinfoCovType data = sw.PersonData(DateTime.Parse(“1-5-1945″), 141919723);

   Assert.AreEqual(0, data.ResultaatcodeCOV);

}

The assertion compares the result with 0. The test will fail.

Again the output clearly tells why the test failed. The return code was not 0 but 6420. The documentation lists all return codes, where 6420 stands for deceased. Which matches the description of the test data on this “person”. To make my code better maintainable I’ve written out a descriptive enumeration of these return codes.

public enum ResultaatRaadpleging : int

{

   OK = 0,

   NietGeauthoriseerdBijAangegevenVerzekeraar = 6400,

   ……

   BSNvakerGevonden = 6419,

   PersoonIsOverleden = 6420

}

Using that to make the test pass:

[Test]

public void IsDeceased()

{

   VecozoLib.ServiceWrapper sw = new ServiceWrapper();

   VecozoLib.VecozoService.RetourinfoCovType data = sw.PersonData(DateTime.Parse(“1-5-1945″), 141919723);

   Assert.AreEqual(VecozoLib.ResultaatRaadpleging.PersoonIsOverleden, (VecozoLib.ResultaatRaadpleging) data.ResultaatcodeCOV);

}

Here I am writing tests which describe the actual desired domain specific functionality of my app. The code I am writing to fulfill the test is code which also fulfills my customers demands. Adding all desired functionality will result in a lot of refactoring. Adding the functionality test by test will make sure that no existing code breaks in the process.

So far the TDD gospel in my words. I hope to have made clear that using TDD as a methodology is nothing mysterious, just an easy way to gain and keep control over the app you are building. Nothing more, nothing less.

<disclaimer>

Since this post was published a lot of people have asked me for the original source code of the project or asked me questions on further details of the Vecozo service. The source code is not my property, I cannot give you that. My knowledge on the actual details of the Vecozo service is not that great either. This post describes a way to explore and test the service. Step by step in a reproducable way. No more. I am sorry but you have to figure out all further detail by yourself. Following a TDD approach that should be no big problem.

</disclaimer>

This entry was posted in Out+of+control. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://codebetter.com/members/pvanooijen/default.aspx pvanooijen

    The source code project is owned by my customer. I cannot share that. Sorry.

  • Niels

    Hi Peter,

    I know that this post is quite dated, but I was wondering if you still had the project somewhere? I am curious to see how it all looks together.

    Bye!