Techquiz Question 2: How to test this code?

2012-04-10    

Question two in our TechQuiz is about Unit Testing.

  1. What do you think is an advantage of Unit Testing?
  2. What is the biggest problem you encounter when writing a Unit Test?
  3. If you have to test the OrderProcessor, how would you do it? How to refactor the code to make it improve testability?
public  class  OrderProcessor
{
   public void ProcessOrder(Order order)
   {
      var creditCardService = new  CreditCardService();
      creditCardService.Charge(order.Price, order.Customer.Id);

      order.IsPaid = true;
   }
}

Answer

To sum it up, Unit Testing helps you with designing, developing and maintaining your code.

A nice list can be found on http://en.wikipedia.org/wiki/Unit_testing#Benefits.

One of the biggest issues when using Unit Testing is not in the Unit Test itself. We can use C# for both writing our tests and the production code and we only have to learn some simple attributes and methods. But most of the time, the problem is in our production code.

Take the example of the OrderProcessor. Because the CreditCardService is constructed inside the method we can't access it. If the service is slow running or needs an actual connection to a credit car company it will be hard to create a fast running unit test without any side effects.

But what if we change the code to the following:

public  class  OrderProcessorService : IOrderProcessorService
{

   public OrderProcessorService(ICreditCardServicecreditCardService)
   {
       this.creditCardService = creditCardService;
   }

   private  ICreditCardService creditCardService;

   public void ChargeOrder(Order order)
   {
       creditCardService.Charge(order.Price, order.Customer.Id);
       order.IsPaid = true;
   }
}

Now we have separated the business logic from constructing a CreditCardService. There is no hard dependency on a implementation of this service and we can easily test it by using a mock:

[TestMethod]
public  void WhenTheOrderIsChargedMyCreditCardShouldBeBilled()
{
    // Arrange
    Customer customer = new  Customer() { Id = 100 };
    Order order = new  Order() { Price = 25.95M, Customer = customer };

    // Act
    ICreditCardService creditCardService =   MockRepository.GenerateStrictMock<ICreditCardService\>();
    creditCardService.Expect(c => c.Charge(order.Price, customer.Id));

    IOrderProcessorService orderProcessorService = new  OrderProcessorService(creditCardService);
    orderProcessorService.ChargeOrder(order);

    // Assert that my credit card is billed for the correct amount

    Assert.IsTrue(order.IsPaid);
    creditCardService.VerifyAllExpectations();
}

When you are writing new code, always ask yourself how can I test this code? Separating business logic from constructing your objects will definitely improve testability.