Singletons to facilate unit tests in a legacy code base. A good idea or not?
Folks, I've a large legacy .Net code base, and I'm trying to introduce Unit Testing to the team. They're good guys but this is all new to them (to be honest it's reasonably new to me too).
One of the problems is the code base makes heavy use of static classes in System.IO, there are extensive in-house libraries of static classes and classes aren't written to interfaces (unless there's an actual design reason to do so).
I'm developing an ease-it-in strategy using NUnit and FakeItEasy.
To address the static class dependencies I've written a tool that generates wrapper classes and interfaces for existing Static Classes. e.g. In a config file I say I want wrappers for System.IO Directory & File
, the tool generates an assembly with code along the lines of . . .
public interface IFile
{
// All Method signatures taken from System.IO.File
}
internal class Fi开发者_运维知识库le
: IFile
{
// All Methods delegate to System.IO.File
}
public interface IIO
{
IFile File {get;}
IDirectory Directory {get;}
}
internal class IO
: IIO
{
public IFile File {get; private set;}
public IDirectory Directory {get; private set;}
public IO()
{
File = new File();
Directory = new Directory();
}
}
public static class IO
{
public IIO Instance {get; set;}
static IO()
{
Instance = new IO();
}
}
The idea is that in the existing code base can be simply updated by a find/replace and adding a new project reference, and that in unit tests you can assign a Mock object to IO.Instance
.
One one hand I'm quite happy with this approach, it gives a clean and fast way to move away from using static classes directly, plus gives us a way to mock those static classes and lets us test code that depends on them.
On the other hand, I kind of feel I've done a deal with the devil, I've replaced dependencies on implicit singletons with dependencies on explicit singletons.
Is this all going to go horribly wrong for me sometime in the near future, or do you think this is a workable approach?
I would do as you did for a legacy application. Not much you can do without having to refactor larger pieces. It's still flexible since you can change implementation through the static class.
One idea could be to only make the Instance method static and register the IO class in your IOC instead (as a singleton). Then let the IOC find the correct implementation (which is returned by the static Instance method).
Nice introduction to IoC containers: http://joelabrahamsson.com/entry/inversion-of-control-introduction-with-examples-in-dotnet
精彩评论