WCF, Mocking and IoC: Oh MY!

Introduction:

I started writing a little post about what I want to do with WCF, and I ended both doing it and documenting it. It will probably be interesting to a small group of people.
Please note that a rudimetry understanding of WCF, IoC and Mocking would probably be helpful, although I made the best to explain most concepts. At times I took some shortcuts by using advance features of all three frameworks, those are usually very well documented, but I am always open for questions.
You can find the sample code here

I need to consume a service provided by a third party, and I would really not like to do production debugging on it. So I will use unit tests to provide me with an assurance that I am handling all the assumption correctly. Like I said, this service is provided by a 3rd party, so I don't care how it is implemented. I do care how easy it is for me to cunsume and use this service, and I like to learn new stuff every now and then, so I decided to use WCF to do this interaction.

Here is the service documentation:

Orders Service - Submit Order:
This operation will take an order message and submit it for processing. Once the order has been successfully submitted, it is the responsability of the service to ensure that it won't be dropped. Clients that use this operation can assume that an order is never lost.
However, if an error occurs during the submittion process, the order is not submitted, and a client must take whatever steps neccesary to recover from that (show an error to the user, log and retry at a later time, etc).

I have a set of tools that I like to work with, and I don't see a reason why using WCF would change that, therefor, I am probably going to have to do some work to integrate WCF into the set of tools that I can use. Let us start by defining the entities we are going to work with:

(Image from clipboard).png

For simplicity sake, I am going to ignore a lot of other interesting properties (shipping address, billing, etc). My recommended way of operation is Test First, so let us try writing the test code for the OrderController:

[TestFixture]

public class OrderControllerFixture

{

       [Test]

       public void CanCreateController()

       {

              IOrderService orderService = null;

              OrderController controller = new OrderController(orderService);

       }

}

I don't have anything else, but I think that I would need an order service and an order controller. The distinction is important. The IOrderService is concerned mainly with the "how" - in this case, submitting an order for processing. The OrderController is concerned mainly with the "why", it takes a request (usually from a user) and turn it into an action with a business meaning.

In this case, I specify that OrderController takes a IOrderService in its constructor. I won't bore you with the details of how I build a class and constructor.  Now, I can test write the next test:

[Test]

public void WillSubmitOrderToService()

{

       MockRepository mocks = new MockRepository();

       IOrderService orderService = mocks.CreateMock<IOrderService>();

       orderService.SubmitOrder(null);

       LastCall.IgnoreArguments();

       mocks.ReplayAll();

      

       OrderController controller = new OrderController(orderService);

       int orderId = 5;

       controller.SubmitOrder(orderId);

 

       mocks.VerifyAll();

}

Couple of things to note here, I am creating a mock of IOrderService, and then I setup an expectation that its SubmitOrder will be called (and I don't care about the arguments). I then create the controller with the mocked order service, and submit an order.

I had to think for a couple of minutes when I wrote the test, what should the controller's SubmitOrder take? I am going to use this over the web, and probably this will be used as the last stage of a wizard. This indicate to me that an integer (primary key) is a good idea, since it is something that can easily be passed from the request/view. This also means that I need some sort of persistent storage, likely a database. For simplicity sake, I am going to hand-wave this, assume that I am using Active Record and that I have already set the data up correctly.

The next decision is what should the order service SubmitOrder should accept. My business requirements are such that it should accept the entire Order object (and all its assoicated objects, such as order lines, products, etc), because the service would also need to print a reciept of the order as well as bill it.

Running the test now will fail, because the SubmitOrder on the controller it empty, I added an simple implementation that made it work:

[Transient]
public

class OrderController
{
       public void SubmitOrder(int orderId)
       {

              orderService.SubmitOrder(null);

       }

}

The job of the next test is to assert that we send a valid order:

[Test]

public void WillSubmitOrderWithSameIdAsTheOnePassedToController()

{

       MockRepository mocks = new MockRepository();

       IOrderService orderService = mocks.CreateMock<IOrderService>();

       orderService.SubmitOrder(null);

       LastCall.Constraints(Is.NotNull() && Property.Value("Id", 5));

       mocks.ReplayAll();

      

       OrderController controller = new OrderController(orderService);

       int orderId = 5;

       controller.SubmitOrder(orderId);

 

       mocks.VerifyAll();

}

The only thing that I changed was the expectation, now I am not satisfied with merely a call to the service's SubmitOrder method, but I also assert that it is not null and that the Id property equals to 5. Running this test lead to the following error:

TestCase 'OrderControllerFixture.WillSubmitOrderWithSameIdAsTheOnePassedToController'
failed: IOrderService.SubmitOrder(null); Expected #0, Actual #1.
IOrderService.SubmitOrder(not equal to null and property 'Id' equal to 5); Expected #1, Actual #0.

You can bet that I am proud of Rhino Mocks for the above message :-) At any rate, let us change the SubmitOrder implementation so the test will pass:

[Transient]
public

class OrderController
{

       public void SubmitOrder(int orderId)

       {

              Order o = GetOrderById(orderId);

              orderService.SubmitOrder(o);

       }

 

       private static Order GetOrderById(int orderId)

       {

              Order o = new Order();

              o.Id = orderId;

              return o;

       }

}

(Remember: I do not really care about the persistance of orders right now. This is simple and works). The tests works, but I see duplication in the tests that I can remove. The new test fixture looks like this:

[TestFixture]

public class OrderControllerFixture

{

       IOrderService orderService;

       MockRepository mocks;

       private OrderController controller;

 

       [SetUp]

       public void TestInitialize()

       {

              mocks = new MockRepository();

              orderService = mocks.CreateMock<IOrderService>();

              controller = new OrderController(orderService);

       }

 

       [TearDown]

       public void TestCleanup()

       {

              mocks.VerifyAll();

       }

 

       [Test]

       public void WillSubmitOrderToService()

       {

              orderService.SubmitOrder(null);

              LastCall.IgnoreArguments();

              mocks.ReplayAll();

             

              int orderId = 5;

              controller.SubmitOrder(orderId);

       }

 

       [Test]

       public void WillSubmitOrderWithSameIdAsTheOnePassedToController()

       {

              orderService.SubmitOrder(null);

              LastCall.Constraints(Is.NotNull() && Property.Value("Id", 5));

              mocks.ReplayAll();

             

              int orderId = 5;

              controller.SubmitOrder(orderId);

       }

}

I removed the CanCreateOrderController, since I have moved it to the setup method anyway. Now, we need to have some way to notify the user, so we create a new test:

[Test]

public void WillReturnTrueIfOrderSubmittedSuccessfully()

{

       orderService.SubmitOrder(null);

       LastCall.IgnoreArguments();

       mocks.ReplayAll();

      

       int orderId = 5;

       bool result = controller.SubmitOrder(orderId);

       Assert.IsTrue(result, "Should return true is send was successful");

}

To make this test pass, I modified the SubmitOrder method to return a boolean and added a "return true" statement at the end of the method. Note that I do not care what I pass to the service's SubmitOrder() here, so I changed the expectation to a less restrictive one, in general, it is a best practice not to specify things that you aren't testing explicitly, otherwise, your tests may be extremely fragile.

Now that I got a passing test for a successful submittion, let us try to see what the failure mode that we have are. As far as I am concerned, there are two failure modes that I can identify at the moment, infrastructure failures (the service is down) or business failures (the submitted order is not valid, for instnace, not enough credit to pay it remained).

Now, let us think about the way WCF work, we have this seperation there as well, we have CommunicationException and FaultException, with the same overall meaning. I am going to make several assumptions now:

  • Asserting that the configuration is correct is not the job of the OrderController, therefor, it assumes that the service it was configure to use is valid.
  • A communication error is trasient in nature and should be retried if they failed, up to a pre-defined limit.
  • A business errors cannot be recovered by a retry.

With those assumptions in mind, I can write the following test:

[Test]

public void WillReturnFalseIfOrderSubmittedRaisedFaultException()

{

       orderService.SubmitOrder(null);

       LastCall.IgnoreArguments()