Kash Farooq's software development blog

.NET Developer

Stubbing RESTful services with WCF (Part 3)

Posted by Kash Farooq on July 9, 2012

A three part series.

  • Part 1: The set-up. Setting up an example to show what we are trying to stub.
  • Part 2: Creating a REST service stub.
  • Part 3: Using the service stub from a test.

In this final part of the series, I’ll use the ServiceStub class created in Part 2 to create a full end-to-end test.

I want to create an Order Query System that uses the data provided by the RESTful URI to return some consolidated data.

Here is a very simple example to start with – the Order Query System provides a method that calculates how old an order is.  My test code needs to bring up the service stub, populate it with some stub data, call the Order Query System and then stop the service stub.:

[TestFixture]
public class TestCode
{
  private ServiceStub serviceStub;

  [TestFixtureSetUp]
  public void TestFixtureSetUp()
  {
    serviceStub = new ServiceStub();
    serviceStub.Start();
  }

  [TestFixtureTearDown]
  public void TestFixtureTearDown()
  {
    serviceStub.Stop();
  }

  [Test]
  public void GetNumbersOfDaysSinceOrderPlaced()
  {
    DateTime orderDate = DateTime.Now.AddDays(-100);
    var order = new Order {Id = 1, OrderDate = orderDate, OrderLinesRef = "http://localhost:7000/OrderingSystem/Order/1/Orderlines" };
    serviceStub.Add(order);

    var orderQuerySystem = new OrderQuerySystem();
    var daysSinceOrderPlaced = orderQuerySystem.GetNumberOfDaysSinceOrderWasPlaced(1);

    daysSinceOrderPlaced.Should().Be(100);
  }
}

I bring the service stub up in the Test Fixture Set-up and close it down in the corresponding tear down method. In the test, I create some dummy data and add it to the stub. I then call the Order Query System  and do my my assert.

And the method in the OrderQuerySystem class:

public class OrderQuerySystem
{
  public int GetNumberOfDaysSinceOrderWasPlaced(int orderId)
  {
    var gateway = new RestServiceGateway();
    var order = gateway.GetOrder(orderId);
    return (DateTime.Now - order.OrderDate).Days;
  }
}

The Order Query System uses the RestServiceGateway (implementation omitted – I’ll give this at the end of the post for completeness) to get the data it needs to fulfil the query.

Here is an example that requires me to navigate to the child entity. I’ll call a Order Query System method that returns the number of order lines that a particular order has – this means I need to stub both the order and the order lines belonging to that order:

[Test]
public void GetNumbersOfOrderLinesForAnOrder()
{
   var order = new Order { Id = 2, OrderDate = DateTime.Now, OrderLinesRef = "http://localhost:7000/OrderingSystem/Order/2/Orderlines" };
   var orderLine1 = new OrderLine { Id = 21, OrderRef = "http://localhost:7000/OrderingSystem/Order/2"};
   var orderLine2 = new OrderLine { Id = 22, OrderRef = "http://localhost:7000/OrderingSystem/Order/2"};
   var orderLine3 = new OrderLine { Id = 23, OrderRef = "http://localhost:7000/OrderingSystem/Order/2"};
   var orderLines = new OrderLines {ArrayOfOrderLines = new[] {orderLine1, orderLine2, orderLine3}};
   serviceStub.Add(order);
   serviceStub.Add(order.Id,orderLines);

   var orderQuerySystem = new OrderQuerySystem();
   var numberOfOrderLines = orderQuerySystem.GetNumberOfOrderLinesFor(2);

   numberOfOrderLines.Should().Be(3);
}

And the corresponding method in the OrderQuerySystem class:

public class OrderQuerySystem
{
  public int GetNumberOfOrderLinesFor(int orderId)
  {
    var gateway = new RestServiceGateway();
    var order = gateway.GetOrder(orderId);
    var orderlines = gateway.GetEntity<OrderLines>(order.OrderLinesRef);
    return orderlines.ArrayOfOrderLines.Length;
  }
}

In the above example, the system under test code has to navigate to a child entity based on a ref provided by the parent entity.

And that’s it. On a recent project I’ve created some rock-solid integration tests using this technique.

For completeness, here is the RestServiceGateway class:

public class RestServiceGateway
{
  private const string EntityAddress = "http://localhost:7000/OrderingSystem/{0}/{1}";
  private const string ChildEntityAddress = "http://localhost:7000/OrderingSystem/{0}/{1}/{2}";

  public Order GetOrder(int orderId)
  {
    return GetEntity<Order>(string.Format(EntityAddress,"Order",orderId));
  }

  public OrderLine GetOrderLine(int orderlineId)
  {
    return GetEntity<OrderLine>(string.Format(EntityAddress, "Orderline", orderlineId));
  }

  public OrderLines GetOrderLinesFor(int orderId)
  {
    return GetEntity<OrderLines>(string.Format(ChildEntityAddress, "Order", orderId, "Orderlines"));
  }

  public T GetEntity<T>(string entityAddress)
  {
    var xmlSerializer = new XmlSerializer(typeof (T));
    var xmlReader = XmlReader.Create(entityAddress);
    var entity = (T) xmlSerializer.Deserialize(xmlReader);
    return entity;
  }
}
Advertisements

Sorry, the comment form is closed at this time.

 
%d bloggers like this: