Don’t unit test the glove

There was an email chain letter that my Mom forwarded to me a while back. It was a math magic trick that could predict your age. She asked me how it worked.

It had you start with the year you were born, multiply by one number, add another, repeat the digits, and go through all sorts of numeric hocus-pocus. If you just worked through it using basic algebra, it's easy to see that you were just subtracting the year of your birth from the current year. The intermediate steps added no value. How could thing predict your age? How could it not?

I'm looking at a unit test for a data access service, and I'm reminded of that email that filled Mom with wonder. The unit test mocks an order repository using Rhino Mocks:

int expectedOrderIdentifier = 5;

Order expectedOrder = new Order();
expectedOrder.OrderId = expectedOrderIdentifier;
expectedOrder.WebOrderNumber = 123;
IList<Order> orderList = new List<Order>();
orderList.Add(expectedOrder);

MockRepository mocks = new MockRepository();
IAggregateRootRepository<Order> orderRepositoryMock =
    mocks.CreateMock<IAggregateRootRepository<Order>>();

using (mocks.Record())
{
    Expect.Call(
        orderRepositoryMock.GetSatisfying(dummyOrderSpecification))
        .IgnoreArguments()
        .Return(orderList.AsQueryable<Order>());
}
using (mocks.Playback())
{
    OrderService target = new OrderService(orderRepositoryMock);
    Order returnedOrder = target.GetOrderByWebOrderNumber(expectedOrderIdentifier);
    Assert.IsTrue(
        returnedOrder == expectedOrder,
        "The mocked repostiory provided order was not returned by the repository changes will not be tracked by the unit of work.");
    Assert.IsTrue(
        returnedOrder.WebOrderNumber == 123,
        "The mocked repostiory provided order was not returned by the repository changes will not be tracked by the unit of work.");

}
// Tests to make sure the order repository was enlisted in the provided context.
mocks.VerifyAll();

Lo and behold the test passes! Amazing! Let's take a look at the code under test:

public Order GetOrderByWebOrderNumber(int webOrderNumber)
{
    // Create Specification 
    // This specification is used to get just the order entity without any dependent entities 
    ReferencedOrderByWebOrderNumberSpecification specification =
        new ReferencedOrderByWebOrderNumberSpecification(webOrderNumber);

    // Here Assumption is we get only one order from database  for the provided web order number 
    Order order = _orderRepository.GetSatisfying(specification).FirstOrDefault();

    if (order == null)
    {
        throw new ArgumentException();
    }

    return order;
}

A perfectly reasonable bit of code. It pulls an object from the repository according to a specified condition. In fact, the only interesting thing about this code is that condition.

But look closer. The unit test passes in the wrong parameter! The method takes a web order number, and the unit test passes in an order ID. The code under test creates a specification based on this incorrect information, but the mock repository ignores this parameter. No matter what the condition is, it returns the object we tell it to. Since we mocked the repository to return the object we expected, that's exactly what was returned. How could the test ever fail?

This unit test is completely meaningless. It goes through all sorts of gyrations and mock-object hocus-pocus in order to return the object that we expect. The one interesting feature of the code under test is never tested. In fact, this test passes even thought the object that we fed the mock repository didn't satisfy the condition!

Like my Mom with the email, the developer who wrote this test really though something interesting was happening. In striving for greater test coverage, he was just fooling himself into thinking that he was improving the quality of the code. Useless unit tests are worse than no unit tests, because they give you false confidence in the code.

A glove is a thin wrapper over a hand. The glove is going to do what the hand does. How could it do any different? Don't waste your time unit testing the glove.

Leave a Reply

You must be logged in to post a comment.