Home > Azure, Dotnet 4.5, Windows Service Bus > Transient faults and testing

Transient faults and testing

In this post I am going to talk about the transient fault exception handling block and how we can unit test using mock. So we start by getting the Transient Fault Handling Application from Nuget.

PM> Install-Package EnterpriseLibrary.TransientFaultHandling

Although in the package description it mentions about windows azure but it can be used for any .Net component. In fact if you are doing any serious .net enterprise development I highly recommend using this in your application. And as it happens to be that I am working on a On-Premise Windows Service Bus and I had to build a robust capability of handling transient faults so I used it for my service bus code as well as for SQL Server, SQL Azure, Blob Storage, SFTP etc.

The one simple rule for using this application block is that you have define an error detection strategy (a class) which implements the ITransientErrorDetectionStrategy interface and then wire it with retry policy class of the application block and that’s it you are up and running.

So the code I am going to write is a simple OrderService which picks up Orders which are in the state of "Not Processed" and takes the Id’s of the orders and send the message to the bus. For brevity I have left all the details and specific and just wrote this demo like code. The main focus of this post is on writing effective unit tests for fail/retry logic.

Lets start cutting some code with our Order POCO class, some interface to mock EntityFramework context and a OrderService which will coordinate the whole task.

OrderStatus.cs

public enum OrderStatus
{
    NotProcessed,
    InTransition,
    Shipped
}

IOrderContext.cs

public interface IOrderContext
{
    DbSet<Order> Order { get; set; }
}

Order.cs

public class Order
{
    public int Id { get; set; }

    // omitted for brevity
    public OrderStatus Status { get; set; }
}

IServiceBus.cs

public interface IServiceBus
{
    void Send(BrokeredMessage messages, string queueName);
}


Now we will define our error detection strategy as following.

MessagingCommunicationErrorDetectionStrategy.cs

public class MessagingCommunicationErrorDetectionStrategy 
                : ITransientErrorDetectionStrategy
{
    public bool IsTransient(Exception ex)
    {
        var commException = 
                    ex as MessagingCommunicationException;

        if (commException != null)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

And this is order service which will fetch some unprocessed orders using EntityFramework and send them to the service bus.

OrderService .cs

public class OrderService : IOrderService
{
    private readonly IServiceBus serviceBus;

    private readonly IOrderContext orderContext;

    public OrderService(IServiceBus serviceBus, IOrderContext orderContext)
    {
        this.serviceBus = serviceBus;
        this.orderContext = orderContext;
    }

    public void Process()
    {
        var orders = orderContext
                        .Order
                        .Where(x => x.Status == OrderStatus.NotProcessed)
                        .ToList();

        var retryPolicy = 
            new RetryPolicy<MessagingCommunicationErrorDetectionStrategy>(5);

        orders.ForEach(o => 
                retryPolicy.ExecuteAction(() => 
                                            this.serviceBus.Send
                                            (
                                                new BrokeredMessage
                                                    {
                                                        MessageId = o.Id.ToString()
                                                    },
                                                    "orderqueue")
                                            ));
    }
}

As you can see for quick demo purpose I have hard coded the creation of the retry policy but in a real production application I would refactor that into an interface and inject it using Dependency Injection.

Now lets write some unit tests to cover following scenarios.

  • Happy case where everything runs smoothly and no exceptions happens.
  • Exception is thrown from our test but retry logic takes care of it.
  • Exception is thrown and all the retry attempts are exhausted.

I am going to use Moq to mock and in-memory list of orders and wire that up to EntityFrameWork DbSet so that when the service asks the OrderContext to get order records, it retrieves it from the in-memory list rather than going to a physical database. In the happy case scenario I will fetch 5 records from the in-memory database and process them and verify that the service bus Send() method is called exactly the same number of times as we have records in the DbSet. However in the second test I will just fetch only one record to verify that the service bus send method is called twice as the first call did failed due to the exception.

WhenUsingOrderService.cs

  [TestClass]
  public class WhenUsingOrderService
  {
      private Mock<IServiceBus> serviceBusMock;

      private Mock<IOrderContext> contextMock;

      private Mock<DbSet<Order>> dbSetMock;

      private OrderService orderService;

      [TestInitialize]
      public void Setup()
      {
          contextMock = new Mock<IOrderContext>();
          dbSetMock = new Mock<DbSet<Order>>();
          serviceBusMock = new Mock<IServiceBus>();
      }

      [TestMethod]
      public void ShouldBeAbleToSendMessage()
      {
          this.BuildDbSetMock(5);
          orderService = new OrderService(serviceBusMock.Object,
                                          contextMock.Object);

          orderService.Process();

          serviceBusMock.Verify(
                              x => x.Send(It.IsAny<BrokeredMessage>(),
                              "orderqueue"),
                              Times.Exactly(dbSetMock.Object.Count()));
      }

      [TestMethod]
      public void ShouldBeAbleToHandleMessagingCommunicationException()
      {
          bool firstTimeExecuteCalled = true;

          serviceBusMock
              .Setup(
                      x => x.Send(It.IsAny<BrokeredMessage>(),
                      "orderqueue"))
              .Callback(() =>
              {
                  if (firstTimeExecuteCalled)
                  {
                      firstTimeExecuteCalled = false;
                      throw new
                          MessagingCommunicationException("CommunicationException");
                  }
              });
          this.BuildDbSetMock(1);
          orderService = new OrderService(serviceBusMock.Object,
                                          contextMock.Object);

          orderService.Process();

          serviceBusMock.Verify(
                              x => x.Send(It.IsAny<BrokeredMessage>(),
                                          "orderqueue"),
                                          Times.Exactly(2));
      }

      [TestMethod]
      public void ShouldBeAbleToReachMaximumRetries()
      {
          int callCount = 1;
          serviceBusMock
                  .Setup(
                          x => x.Send(It.IsAny<BrokeredMessage>(),
                          "orderqueue"))
                  .Callback(() =>
                  {
                      if (callCount < 5)
                      {
                          callCount++;
                          throw new MessagingCommunicationException("CommunicationException");
                      }
                  });
          this.BuildDbSetMock(1);
          orderService = new OrderService(serviceBusMock.Object,
                                          contextMock.Object);
          try
          {
              orderService.Process();
          }
          catch (Exception ex)
          {
              Trace.WriteLine(ex.Message);
          }

          Trace.WriteLine(string.Format("Reached the maximun reties count of {0}", callCount));
          
          serviceBusMock.Verify(
                              x => x.Send(It.IsAny<BrokeredMessage>(),
                                          "orderqueue"),
                                          Times.Exactly(5));
      }

      private void BuildDbSetMock(int numberOfOrders)
      {
          var orders = OrderDataProvider.GetOrders()
                                        .Take(numberOfOrders)
                                        .AsQueryable();

          dbSetMock.As<IQueryable<Order>>()
                   .Setup(m => m.Provider)
                   .Returns(orders.Provider);

          dbSetMock.As<IQueryable<Order>>()
                   .Setup(m => m.Expression)
                   .Returns(orders.Expression);

          dbSetMock.As<IQueryable<Order>>()
                   .Setup(m => m.ElementType)
                   .Returns(orders.ElementType);

          dbSetMock.As<IQueryable<Order>>()
                   .Setup(m => m.GetEnumerator())
                   .Returns(orders.GetEnumerator());

          contextMock.Setup(x => x.Order)
                     .Returns(dbSetMock.Object);
      }
  }

As you can see, it’s a pretty slick test to cover up all the scenarios and I am using the Callback() method in our mock setup to create failure scenarios and throw exception. The complete source code is available at my skydrive.

Advertisements
  1. December 1, 2014 at 4:56 am

    Reblogged this on Dinesh Ram Kali..

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: