Saturday, April 21, 2012

Worktime music: the high-tech device


If you interested in mastering this captivating art – there is a comprehensive training video at your service:

Wednesday, April 18, 2012

Unit testing legacy code the lazy way

When it comes to maintaining or expanding a legacy code, it is no surprize that we often find ourselves struggling with a mess of magically changing global variables, 1000-lines long methods, which do hundred different things inside and keep all logic to itself. Sometimes changed requirement or just popped production bug demand a limited incision – and often you afraid to touch the fragile masses of code around you and just try to minimize the potential impact and hope for the best.
More often than not method you need to change was developed in unenlightened age, when people used computers made of wood did not concern themselves with principles of cohesion or separation of concerns or thoughts about testability. It is hopelessly long and complex and business functionality is mixed with calls to database or services. Testing such a method is a dreadful task, which easily can eat a lot of time and bring the wrath of management on your head. But it still is no excuse to leave this code without any test coverage – so try to cover at least functionality which you are adding or even affecting. There are tricks you can use to test the legacy code fore cheap and with little overhead, quickly getting to the important logic testing. Let’s consider the following (greatly simplified) legacy method.

public bool ProcessEligibility(Person person)
{
    // Some functionality which we leave outside of our testing
    // A bunch of business logic which we would like to test
    bool result = CallDB(new ConnectionManager(), person.Age, person.Position);
    //Another bunch of business logic which we would like to test
    ServiceResponse response = RequestService("http://real.com/svc", person);
    return response.IsSuccessful;
}
It contains some logic which we would like to test, but also two calls to the complex methods, on of which hits database – CallDB and another, which calls outside service – RequestService. Those we would like to keep aside and maybe test them in separation, if time permits.
Let’s use Template Method derivative to minimize our testing efforts. Our goal is convert undesired method to dependencies which can be injected to improve testability.
First, let’s refactor the functionality, which we will test, out to a protected method:
public bool ProcessEligibility(Person person)
{
    // Some functionality which we leave outside of our testing
    return ProcessEligibilityConcrete(person);
}

protected bool ProcessEligibilityConcrete(Person person)
{
    // A bunch of business logic which we would like to test
    bool result = CallDB(new ConnectionManager(), person.Age, person.Position);
    //Another bunch of business logic which we would like to test
    ServiceResponse response = RequestService("http://real.com/svc", person);
    return response.IsSuccessful;
}
Next we lay a foundation for our methods to be extracted as a parameters. We can use a Func<T1, T2, TOutput> type to describe functions in our specific case, but unfortunately Func delegate allows maximum three input parameters while old-fashioned methods can accept 10 or more! In this case we have to create a delegate type ourselves:
public delegate TOutput HyperFunc<T1, T2, TOutput>(T1 input1, T2 input2);
Now let’s refactor the main and sub-methods further, injecting methods as dependencies (let’s describe one as a Func<T1, T2, TOutput> and another as a custom delegate):
public bool ProcessEligibility(Person person)
{
    // Some functionality which we leave outside of our testing
    return ProcessEligibilityConcrete(person, CallDB, RequestService);
}

protected bool ProcessEligibilityConcrete(Person person, 
    Func<ConnectionManager,int,string,bool> dbCaller,
    HyperFunc<string,Person,ServiceResponse> serviceCaller)
{
    // A bunch of business logic which we would like to test
    bool result = dbCaller(new ConnectionManager(), person.Age, person.Position);
    //Another bunch of business logic which we would like to test
    ServiceResponse response = serviceCaller("http://real.com/svc", person);
    return response.IsSuccessful;
}

public delegate TOutput HyperFunc<T1, T2, TOutput>(T1 input1, T2 input2);
Now we can test the ProcessEligibilityConcrete method in isolation, providing our own mock or stub methods as substitution:
[TestMethod]
public void TestEligibility()
{
    var result = ProcessEligibilityConcrete(new Person(), fakeDbCaller, fakeServiceCaller);
    Assert.IsTrue(result);
}
I said at the beginning that we got this test coverage for cheap. It is not quite true: we paid with adding redundancy and some obscurity to the original code. We performed some refactoring but we clogged the final result a bit, reducing code readability, although all refactorings were safe, especially if you used tools like Resharper.
To do it right, we should do a real refactoring and use mocking or other more sophisticated techniques. But between Fast, Good and Cheap we can pick only any two and weight out our trade-offs.


© 2008-2013 Michael Goldobin. All rights reserved