Breaking Hidden Dependencies

This is a part of my series on practising TDD while working with legacy code. The first problem most developers encounter when trying to get a class under test is hidden dependencies. It is what usually forces most developers to give up their TDD initiative and conclude that unit testing legacy code is next to impossible.

Seams and Dependencies

The main reason legacy code is hard to test is because it is hard to get at the hidden dependencies of a class. The class you are trying to change might depend on a database or file streams or an email server or on twenty modules with their own dependencies. The setup for a test trying to initialise all these dependencies might be 50 lines of code or involve setting up a new test database. To make unit testing viable, you have to break these dependencies and make the code under test more loosely coupled. I don’t want to log or save something to the database while testing a unit of code. I only want to test the logic in that unit. To achieve this I need to be able to create a fake database (or a fake logging class) that doesn’t interfere with my test.

Another reason to try and break dependencies is to use them for sensing. Sometimes the class under test affects other coupled classes but we have no way of seeing the result. If we can get at the dependent class then we can see what message the class under test tried to send.

The first problem is getting at the dependency so that I can swap it out or disable it. It could be buried a few levels down in the code and be inaccessible to me when I new up the object I want to test. I need to find a seam; a point in the code where I can write tests or make a change to enable testing. I work mostly with C# (and JavaScript but this stuff is way easier there) so if I want to test without changing the code then I usually have three options.

The Easy Option

The first option is to write a test for the class and see it if works. If it doesn’t then for all the parameters into the constructor I trying passing null instead of a value. And if there are still dependencies causing problems then I can try setting them to null via a property (if the dependency is a public property). This works very occasionally but usually I have to do more work than this to get the class under test.

orderService = new OrderService(null, null, null, dependencyICareAbout);

The Test Without Changing Any Code Option

The second option is a pattern from Michael Feathers (again!) called Subclass and Override. This pattern uses inheritance to nullify the dependencies of a class that I don’t care about and allows me to get at dependencies I do care about. There are a lot of variations on this pattern so once you understand it then you can add a bunch of new tools to your refactoring toolbox.

To test a class I can create a new test class that inherits from it (a subclass) and then override methods to do something else. This test class is not part of the production code and is only for testing purposes. An example would be a class that has a dependency on the file system and that has a method that saves data to a file.

public void SaveOrder(int orderId)
{
    Order order = orderRepository.GetOrderById(orderId);
    GetOrderChanges();
    SaveOrderToFile(order);
}

public virtual void SaveOrderToFile(Order order)
{
    //File stream stuff
}

I could then override this method to do nothing and test the rest of class without worrying about data being written to files. So in my example, I can write a test for the SaveOrder method (using my new TestOrderService class) to test the first two lines and not worry about data being saved to a file in the SaveOrderToFile method.

public class TestOrderService: OrderService
{
    public override void SaveOrderToFile(Order order)
    {
        //Do nothing
    }
}

A subtle variant of this pattern is changing the access modifier of a method from private to protected so that you can inherit the method in a test class and get access to it. In C# you will also have to make the method virtual to be able to override it.

The Subclass and Override pattern can also be used to inject a fake class instead of a dependency or to get at other internals to be able to sense if our test succeeded or failed. It is a very powerful pattern especially if you use one of the variations of it called Extract and Override (Call, Factory Method etc.).

The Making Code Testable First Option

The third option is to make the code testable by making some changes to the code that open up the class for testing but do not change any logic. This sounds risky and it is, so to avoid any unpleasant side-effects I try and make any changes as small and safe as possible. Using a tool like Resharper and methods like Extract Method (from Martin Fowler’s classic book, Refactoring) is how I usually accomplish this. There are lots of different techniques for making your code more testable but I will start us off by showing how to use the Extract and Override Call pattern (from Michael Feathers once again).

Here is a typical hard to test method. I need to write a test to check that the email subject and body are generated correctly but if I call this method it will try to send an email (or throw an exception if I don’t have an email server).

public void SendOrderConfirmationToCustomer(int orderId)
{
    Order order = orderRepository.GetOrderById(orderId);

    var email = new MailMessage(DefaultSender, order.CustomerEmailAddress)
                    {
                        Subject = "Order Confirmation",
						Body = BuildOrderConfirmationMessage()
                    };
    smtpClient.Send(email);
}

The first step is to extract the last line into a new method. And then make this new SendEmail method protected and virtual.

public void SendOrderConfirmationToCustomer(int orderId)
{
    Order order = orderRepository.GetOrderById(orderId);

    var email = new MailMessage(DefaultSender, order.CustomerEmailAddress)
                    {
                        Subject = "Order Confirmation",
						Body = BuildOrderConfirmationMessage()
                    };
    SendEmail(email);
}

protected virtual void SendEmail(MailMessage email)
{
    smtpClient.Send(email);
}

The second step is to introduce a new testing subclass that records the message to be sent by saving it in the SentMessage property. This property is for testing purposes only.

public class TestOrderService: OrderService
{
    public MailMessage SentMessage { get; set; }

    protected override void SendEmail(MailMessage email)
    {
        SentMessage = email;
    }
}

Now I can write a unit test for the SendOrderConfirmationToCustomer method and confirm that the Subject is set to “Order Confirmation”.

[Test]
public void SendOrderConfirmationToCustomer_WithAnOrderThatExists_ShouldSendAnEmailWithSubjectSetToOrderConfirmation()
{
    var orderService = new TestOrderService();

    orderService.SendOrderConfirmationToCustomer(1);

    Assert.That(orderService.SentMessage.Subject, Is.EqualTo("Order Confirmation"));
}

Conclusion

I have shown how to override a method to nullify it for testing purposes and also how to do sensing in an overridden method (recording the sent message). The Subclass and Override pattern allows me to test a class without changing anything and so is a very low risk. The Extract And Override Call pattern requires some changes to the code but I would still classify it as low risk. It has the added bonus of improving the code slightly and is the easiest way to break up large methods and the first step to extracting low level code into separate classes.

I use both the Subclass and Override pattern and the Extract and Override Call pattern quite often while working with Legacy Code. They are really great, both for beginners and for programmers used to working with legacy code. I use these patterns less now as I tend to use mocking frameworks and other techniques to help test my code. But all mocking frameworks are basically just a variation of the Subclass and Override pattern.

But we still have one problem left with our SendOrderConfirmationToCustomer method; it fetches an order from the database. How can we test that in a simple way? See my next blog post about Dependency Injection for a description of how we can build on our test/fake classes to make legacy code testable.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s