Sunday, April 28, 2013

Unit testing code with Web service references

My favorite way of working with remote methods is to have an interface describing the contract between server and client. The contract is then placed into a shared assembly referenced by both publisher and consumer. This forces everyone to align to the contract and react immediately when it changes. Another advantage that is relevant here is that the contract introduces abstraction that the service implements. It is unfortunately not always possible to have a contract as a normal reference.

Many applications contain web references for old ASMX web methods in a form of Visual Studio auto-generated code that is based on the service's WSDL. This code contains a proxy class (NotificationServiceSoapClient in the example below) that encapsulates invocation of remote methods. I will try to describe how to refactor a code that uses these proxy objects so it can be unit-tested, of course without connecting to the real Web service.

A trick is to create an interface on the client side and make the proxy class implement the interface. Fortunately, the auto-generated proxy class is partial, so we can define another part of the class:

public interface INotificationService
{
  Result Register(Token t, string listenerUri);
  Result Unregister(Token t);
}

public partial class NotificationServiceSoapClient : INotificationService
{
  //no need to add anything
}


Then, create a service consumer that is dependent on the interface and not on the concrete class.

public class ServerOperation
{
  private readonly INotificationService _notificationService;

  public ServerOperation(INotificationService notificationService)
  {
    _notificationService = notificationService;
  }

  public Token Register(string url)
  {
    var token = new Token { Id = Guid.NewGuid().ToString() };
    var registrationResult = _notificationService.Register(token, url);

    if (!registrationResult.IsSuccessful)
      throw new RegistrationException(registrationResult.ErrorCode);

    return token;
  }
}


Writing a unit tests now is fairly easy.

using Moq;
using NUnit.Framework;

namespace NotificationSystemSpecification
{
  [TestFixture]
  public class ServerOperationSpecification
  {
    [Test]
    public void ShouldThrowExceptionWhenServerRegistrationIsUnsuccessful()
    {
      //GIVEN
      var unsuccessfulResult = new Result { IsSuccessful = false };
      var serviceMock = new Mock<INotificationService>();
      serviceMock.Setup(
                  m => m.Register(It.IsAny<Token>(), It.IsAny<string>()))
                 .Returns(unsuccessfulResult);
      var serverOperation = new ServerOperation(serviceMock.Object);

      //WHEN-THEN
      Assert.Throws<RegistrationException>(
                  () => serverOperation.Register(It.IsAny<string>()));
    }
  }
}