3

I have the following logic in a more than decade old code for which I have to write Unit Tests. It is a concrete class and the following logic lies in the ctor. Is there a good way to write Unit Tests/Mocks for such legacy code. I am using MSTest / RhinoMocks framework and VS 2010 IDE with .Net framework 4.0

public class SomeClass
    {
        /// ctor
        public SomeClass(XmlNode node)
        {
            //Step 1: Initialise some private variable based on attributes values from the node

            //Step 2: Lot of If , else -if statements ---> something like - 

            if (/*attributeValue is something*/)
            {
                // Connect to Db, fetch  some value based on the attribute value. 
                // Again the logic of connecting and fetching is in another concrete class
            }
            else if (/*attributeValue is somthing else*/)
            {
                // fetch a value by loading a config file (this loading and reading of config file 
                // is again a singleton class where config file path is hardcoded)
            }
            else
            {
                // set some private member variable 
            }
        }
    }
1
  • 1
    If you can't refactor for testability, you might look at a product like TypeMock Isolator. It will cost you $$$ though... Commented Sep 5, 2011 at 20:42

2 Answers 2

6

Unit testing legacy code is tricky. In general you will have to refactor first to be able to write unit tests. Your best bet are very small refactoring steps, that one by one improve testability while leaving the class under test in a "working" condition. I would recommend:

1.) Introduce "sensing" variables that allow you to verify the internal state of the class under test at key positions (i.e. before and after the DB call). This will allow you to write tests that verify the current behavior of the class (based on the public sensing variables) without having to refactor very much. Verify the behavior of the class based on these tests and start to refactor. The sensing variables are temporary and should be removed once you have finished your refactorings. They are only there to be able to write tests in the mean time so you somewhat safely refactor.

2.) One by one replace references to concrete classes to interface references that you pass via the constructor. For the singleton you have two options, one is to have the Singleton return a special instance for unit testing - this requires modifying the Singleton implementation but leaves your class under test unchanged. You can do this until you can refactor to use an interface dependency to replace the Singleton.

Also I would recommend picking up a copy of Working Effectively with Legacy Code which describes this step by step refactoring and especially dependency-breaking techniques in detail.

Sign up to request clarification or add additional context in comments.

2 Comments

+1 for the sensing variables. I've had good results implementing this through a constructor injected logging interface that for testing would use a simple memory logger that you can assert against.
The problem is that if you refactor first, you can not be sure that you provide the same functionality as the original code. IMHO test first then refactor, although that can be incredibly hard -- even impossible at times.
1

In addition to what BrokenGlass said, you may want to consider writing a few integration tests to ensure that the overall process works properly. For example, if your application updates some rows in a database, write repeatable tests that go against a test database so that you can continue to ensure proper functionality while you refactor and break the entire thing down into loosely-coupled, testable chunks.

There's nothing worse than refactoring a class, writing a bunch of tests for it, and then realizing that your refactoring broke something else in the application.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.