Question two in our TechQuiz is about Unit Testing.
- What do you think is an advantage of Unit Testing?
- What is the biggest problem you encounter when writing a Unit Test?
- 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.