Every once in a while you encounter some crusty, old, calcified edifice of code that seems to defy all your attempts at change and proves equally hard to test.

Having recently crossed paths with some code of this nature, I pulled Michael Feathers Working Effectively with Legacy Code from my book shelf and re-introduced myself to three indispensable refactorings.

Not only did these techniques and strategies help me with my legacy code, they served as as good reminders for design maxims for new stuff.

1. Extract Interface and parameterize constructor.

This technique has become so widely used it’s one we almost take for granted today.

Say you have a class and you just want to assert that something happens in side on a particular method (or conversely you want it to do something like return a specific value).

    public class InvoiceService
    {
        InvoiceDto GetInvoice(int id)
        {
            // arg! how can I test that this happened?
            WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.OK;

            return new InvoiceTask().GetInvoice(id);
        }

    }

One way to get your hands on that pesky object is to create it externally and then pass it in through the constructor.

    public interface IWebOperationContext
    {
        HttpStatusCode StatusCode { get; set; }
    }

    public class InvoiceService
    {
        private IWebOperationContext ctx;

        // inject via constructor
        public InvoiceService(IWebOperationContext ctx)
        {
            this.ctx = ctx;
        }

        public InvoiceDto GetInvoice(int id)
        {
            // ahhh - this I can make assertions on
            ctx.StatusCode = HttpStatusCode.OK;

            return new InvoiceTask().GetInvoice(id);
        }

    }

Now when you create your test class, you can inject your own version of that class or use a mocking framework (like Moq for C#) to track that certain methods on your object get called.

    public class InvoiceServiceTest
    {
        [TestFixture]
        public class When_calling_the_invoice_service
        {
            private InvoiceService service;
            private Mock<IWebOperationContext> mockContext;

            [SetUp]
            public void SetUp()
            {
                mockContext = new Mock<IWebOperationContext>();
                service = new InvoiceService(mockContext.Object);
            }

            [Test]
            public void Should_be_able_to_get_N_invoice()
            {
                service.GetInvoice(1);
                
                // verify this happened
                mockContext.VerifySet(x => x.StatusCode = HttpStatusCode.OK);
            }

        }
    }

This is one of the easiest refactorings you can do to get your hands into legacy code and is used extensively in all sorts of Java, C#, and Ruby frameworks.

2. Extract and override Factory Method.

While extract interface should often be your first line of defense, sometimes you can’t create a new interface (or the constructor may be off limits).

In cases like these try extracting and overriding a factory method.

Say you have a class where the object initialization takes place in the constructor.


    public class UserService
    {
        private TransactionManager tm;

        public UserService()
        {
            // this is hard to test
            tm = new TransactionManager();
        }
    }

This can be a real pain because if you want to change the behavior of something inside, you have no obvious way to get your hands on it.

By creating a factory method for the object of desire however, you can create a subclass for the class under test, and then override the desired functionality there.


    public class UserService
    {
        private TransactionManager tm;

        public UserService()
        {
            // create a factory method
            tm = CreateTransactionManager();
        }

        protected virtual TransactionManager CreateTransactionManager()
        {
            return new TransactionManager();
        }
    }

    public class TestUserService : UserService
    {
        // and override here in the subclass
        protected override TransactionManager CreateTransactionManager()
        {
            return new FakeTransactionManager();
        }
    }

Now when we go to test our class, we can use our TestUserService, which will behave just like our real one, only we can control what we want to happen with any internal objects (like Transaction Manager).

3. Adapt Parameter.

When you can’t extract the interface, and the parameter you want to test is difficult to fake, try using adapt parameter.

The idea here is to take a method parameter that is hard to test, and wrap it in another where you can basically have it do whatever you want.

For example say have have a notoriously hard object with work with like Java’s HttpServletRequest. And you want a way of controlling what goes on with that object.


public class RequestDispatcher
{
    // hard to test method parameter
    public void populate(HttpServletRequest request)
    {
        String [] values = request.getParameterValues(pageStageName);

        if (values != null && values.length > 0)
        {
            marketBindings.put(values[0]);
        }
    }
}

Create a new interface (called ParameterSource) and use it as a wrapper for the method under test.


public class RequestDispatcher
{
    // hard to test method parameter
    public void populate(ParameterSource source)
    {
        String [] values = source.getParameterForName(pageStageName);

        if (values != null)
        {
            marketBindings.put(values[0]);
        }
    }
}

The concrete production version of your new class would look something like this:


public class ServletParameterSource implements ParameterSource
{
    private HttpServletRequest request;

    public ServletRequestSource(HttpServletRequest request) {
        this.request = request;
    }

    String getParameterValue(string name) {
        String[] values = request.getParameterValues(name);
        if (values == null || values.length <1)
            return null;
        return values[0];
    }
}

And you are free to create a fake one for testing purposes too.

Note: This technique is slightly more risky in that you are changing a methods external API. It may be worth it however if you need to make changes and want some tests backing you up.

So there you have it. Three simple techniques for making your code more testable.

These and many more great refactorings can be found in Michael Feathers excellent book Working Effectively with Legacy Code (highly recommended reading for any aspiring programmer).