C# - Unit Testing/Mocking - Legacy Code
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 i开发者_StackOverflow社区s 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
}
}
}
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.
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.
精彩评论