Paul Gielens:ThoughtsService

another Endpoint to my thoughts

News

Syndication

Ads


Favorites

Projects

April 2005 - Posts

Working Effectively with Legacy Code on the Beach

The last few weeks our team spend a great deal of effort in testability, continues integration and refactoring (CruiseControl.NET totally rocks). Stefan got test infected which led to interesting discussions whether we should, since we are so committed to Unit Testing, go al the way and practice Test Driven Development (TDD). I strongly believe TDD would benefit and fit nicely in our project.

The second iteration of our application has gone live today and I’m currently on my way to the airport for a flight that takes me and Kitty to Gran Canaria. I’ll bring the April and May issues of MSDN Magazine and “Working Effectively with Legacy Code, Feathers” (thanks to Roy) along with me. So in case you’ll see a guy, on the beach, reading a book with this cover…


Working effectively with legacy code

...then you’ll know it’s me. I’ll invite you to have a drink with me :D

I’m out of office till 2th May.

Improving the Testability of Legacy Code using TypeMock.NET

Download the source-code accompanied by this article

 

On an average workday I see the developers surrounding me debugging. Debugging code sounds perfectly normal right? Imagine you are working on a new use-case and your work uses some of the classes/sub-systems already written by others on your team. Do you, while you are debugging your own code, step through code in the classes you’re using? Debugging code other then the newly written is a waste of time right?

 

While I churn out new code I want to be confident that it works as I expect. I’d like to document the intent I have with the newly written code, but don’t feel like writing documentation. I want to be sure that the classes my code depends on are free of any defects. At least in the way that I am using them.

 

And thus I design for testability and unit-test my code, but what about the dependencies between my code and the code written by others?

 

Lets explain ‘Design for testability’ by an example. We are assigned to a use-case where we need to look up a salary worker its authorizations. In this simple example we identify two system functionalities namely ‘Salary’ and ‘Sending in files’. The functionality salary is exposed by a set of web forms to fill in and submit the information. The functionality sending in files (where a file contains salary information in structured XML or in tabular form .xsl) is exposed through a file upload form.

 

We start out with a Data Access class which returns all given authorizations for the specified salary worker.

 

public class AuthorisationsDA

{

       public XmlDocument GetAuthorisations(string salaryWorkerId)

       {

              XmlDocument result = new XmlDocument();

              XmlNode authorisations = result.CreateElement("Authorisations");

              result.AppendChild(authorisations);

 

              XmlNode authorisation = result.CreateElement("Authorisation");

              authorisations.AppendChild(authorisation);

 

              XmlNode id = result.CreateElement("Id");

              id.InnerText = salaryWorkerId;

              authorisation.AppendChild(id);

 

              XmlNode name = result.CreateElement("Functionality");

              name.InnerText = "SAL";

              authorisation.AppendChild(name);

 

              return result;

       }

}

 

Yes, the GetAuthorisations method implementation is fake :D

 

Next up is the consumer of the AuthorisationsDA class namely the Authorisations class.

 

public class Authorisations

{

       public Authorisations()

       {

       }

 

 

       public bool IsAuthorisedFor(string salaryWorkerId, string functionality)

       {

              AuthorisationsDA da = new AuthorisationsDA();

              XmlDocument authorisations = da.GetAuthorisations(salaryWorkerId);

              XmlNodeList functionalities = authorisations.DocumentElement.SelectNodes("Authorisation/Functionality");

 

              foreach (XmlNode authorisation in functionalities)

                     if (authorisation.InnerText == functionality)

                           return true;

 

              return false;

       }

}

 

The Authorisations class contains the business logic to determine whether a given salary worker is authorized for the specified functionality. It does that by iterating over the collection of functionality authorizations returned by the GetAuthorisations method and picking those of interest. And finally the client code.

 

static void Main()

{

       Authorisations authorisations = new Authorisations();

       bool salFunctionality = authorisations.IsAuthorisedFor("1", "SAL");

       bool formsFunctionality = authorisations.IsAuthorisedFor("1", "FRM");

       Console.WriteLine("Salary authorization: {0}",

                                 salFunctionality.ToString());

       Console.WriteLine("Sending in forms authorization: {0}",

                                 formsFunctionality.ToString());

 

       Console.ReadLine();

}

 

A pretty simple example right?

 

Assuming the AuthorizationsDA class is already present in our project but the author didn’t provide any unit-tests nor did he design the class with testability in mind. We could certainly crunch out the code for the IsAuthorizedFor method but where would that leave us? It should be clear that the IsAuthorisedFor method has a dependency towards the AuthorizationsDA class. What if it fails? Recently I experienced this scenario in one of my projects and as long as you have a team of skilled developers (which was the case for this project) you are fine. Nevertheless I am convinced you should refactor existing code or come up with a clever alternative to improve testability. Lets assume your program manager allocates time and resources for you to come up with a more “test friendly” design. There is a concept called Dependency Injection we’ll use to get rid of the AuthorisationsDA dependency. Lets go straight to the code shall we!

 

First we declare an interface for the AuthorisationsDA class.

 

public interface IAuthorisationsDA

{

       XmlDocument GetAuthorisations(string salaryWorkerId);

}

 

Using this interface we can mock up our own authorizations data access component to take control over the data it returns. In order to do so we’ll need to add an additional contructor on the Authorizations class. This contructor will be used to ‘inject’ any dependency exposed through the IAuthorisationsDA interface.

 

public class Authorisations

{

       private IAuthorisationsDA da;

 

 

       public Authorisations()

       {

              da = new AuthorisationsDA();

       }

 

 

       // ctor for dependency injection

       public Authorisations(IAuthorisationsDA authorisationsDA)

       {

              da = authorisationsDA;

       }

 

 

       public bool IsAuthorisedFor(string salaryWorkerId, string functionality)

       {

              XmlDocument authorisations = da.GetAuthorisations(salaryWorkerId);

              XmlNodeList functionalities = authorisations.DocumentElement.SelectNodes("Authorisation/Functionality");

 

              foreach (XmlNode authorisation in functionalities)

                     if (authorisation.InnerText == functionality)

                           return true;

 

              return false;

       }

}

 

The Authorizations class doesn’t look that much different from the previous version besides for the extra constructor. Since we didn’t change the interface of the Authorizations class the client code remains exactly the same. The guts of this example lies in the test harness code. We start out to mock away the AuthorizationsDA class by implementing the IAuthorizationsDA interface.

 

public class AuthorisationsDAMock : IAuthorisationsDA

{

       public XmlDocument GetAuthorisations(string salaryWorkerId)

       {

              XmlDocument result = new XmlDocument();

              XmlNode authorisations = result.CreateElement("Authorisations");

              result.AppendChild(authorisations);

 

              XmlNode authorisation = result.CreateElement("Authorisation");

              authorisations.AppendChild(authorisation);

 

              XmlNode id = result.CreateElement("Id");

              id.InnerText = salaryWorkerId;

              authorisation.AppendChild(id);

 

              XmlNode name = result.CreateElement("Functionality");

              name.InnerText = "SAL";

              authorisation.AppendChild(name);

 

              return result;

       }

}

 

The mocks GetAuthorizations method implementation is similar to the original one but remember… that one was fake! As soon as we inject this mock instance in the Authorizations class we have a clear view on the GetAuthorizations method’s outcome. The unit-tests supplied in the AuthorisationsTests class demonstrate the usage of the new Dependency Injection constructor.

 

[TestFixture]

public class AuthorisationsTests

{

       private Authorisations authorisations;

 

 

       [SetUp]

       public void Setup()

       {

              IAuthorisationsDA mock = new AuthorisationsDAMock();

              authorisations = new Authorisations(mock);

       }

 

 

       [Test]

       public void IsAuthorisedForFunctionality()

       {

              Assert.IsTrue(authorisations.IsAuthorisedFor("1", "SAL"));

       }

 

 

       [Test]

       public void NotAuthorisedForFunctionality()

       {

              Assert.IsFalse(authorisations.IsAuthorisedFor("1", "FRM"));

       }

}

 

We’re set! I personally love this design, not only because its my design but because it clearly expresses my initial intent. This improves the testability of the Authorizations class business logic drastically. Unfortunately the Authorizations class is used throughout several projects, lets assume that my program manager has the excuse that it takes far to much time to write tests and doesn’t want to spend to much resources on any existing code and other members on my team never heard of dependency injection and keep commenting out my constructor. I as a developer should take responsibility for every line of code I write but is it my responsibility as well to make sure my code’s dependencies are doing what they are supposed to do? You bet! But wait there’s an alternative namely TypeMock.NET. TypeMock.NET uses a concept called Aspect Oriented Programming which intercepts calls to the AuthorizationsDA class and letting me control the results. TypeMock.NET thus eliminates the need to refactor and restructure code just to make it testable. If only the author of the AuthorizationDA class had testability in mind while designing his code we then didn’t have the need for a tool like TypeMock.NET in the first place. In an ideal world I would have refactored the code to reflect the second example but in this world (under the conditions I just mentioned) we’ll use TypeMock.NET. We’ll base this example on the code as presented in the first example which had no interface for the AuthorizationsDA class and no special constructor for the sake of Dependency Injection in the Authorizations class. We’ll start out with the code in the test harness.

 

[TestFixture]

public class AuthorisationsTests

{

       private Authorisations authorisations;

 

 

       [SetUp]

       public void Setup()

       {

              MockManager.Init();

              authorisations = new Authorisations();

       }

 

 

       [Test]

       public void IsAuthorisedForFunctionality()

       {

              Mock authorisationsDAMock = MockManager.Mock(typeof (AuthorisationsDA));

 

              authorisationsDAMock.ExpectAndReturn("GetAuthorisations", GetAuthorisationsTestData());

              Assert.IsTrue(authorisations.IsAuthorisedFor("1", "SAL"));

 

              MockManager.Verify();

       }

 

 

       [Test]

       public void NotAuthorisedForFunctionality()

       {

              Mock authorisationsDAMock = MockManager.Mock(typeof (AuthorisationsDA));

 

              authorisationsDAMock.ExpectAndReturn("GetAuthorisations", GetAuthorisationsTestData());

              Assert.IsFalse(authorisations.IsAuthorisedFor("1", "FRM"));

 

              MockManager.Verify();

       }

 

 

       private XmlDocument GetAuthorisationsTestData()

       {

              XmlDocument result = new XmlDocument();

              XmlNode authorisations = result.CreateElement("Authorisations");

              result.AppendChild(authorisations);

 

              XmlNode authorisation = result.CreateElement("Authorisation");

              authorisations.AppendChild(authorisation);

 

              XmlNode id = result.CreateElement("Id");

              id.InnerText = "1";

              authorisation.AppendChild(id);

 

              XmlNode name = result.CreateElement("Functionality");

              name.InnerText = "SAL";

              authorisation.AppendChild(name);

 

              return result;

    &