dan*_*dan 8 .net unit-testing dependency-injection
我正在尝试制作一个非常大,非常遗留的项目.
我们的大多数代码都使用了许多静态可用的服务.问题是这些很难嘲笑.他们曾经是单身人士.现在它们是伪单体 - 相同的静态接口,但是函数委托给可以切换的实例对象.像这样:
class ServiceEveryoneNeeds
{
public static IImplementation _implementation = new RealImplementation();
public IEnumerable<FooBar> GetAllTheThings() { return _implementation.GetAllTheThings(); }
}
Run Code Online (Sandbox Code Playgroud)
现在在我的单元测试中:
void MyTest()
{
ServiceEveryoneNeeds._implementation = new MockImplementation();
}
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好.在prod中,我们只需要一个实现.但测试并行运行,可能需要不同的模拟,所以我这样做:
class Dependencies
{
//set this in prod to the real impl
public static IImplementation _realImplementation;
//unit tests set these
[ThreadStatic]
public static IImplementation _mock;
public static IImplementation TheImplementation
{ get {return _realImplementation ?? _mock; } }
public static void Cleanup() { _mock = null; }
}
Run Code Online (Sandbox Code Playgroud)
然后:
class ServiceEveryoneNeeds
{
static IImplementation GetImpl() { return Dependencies.TheImplementation; }
public static IEnumerable<FooBar> GetAllTheThings() {return GetImpl().GetAllTheThings(); }
}
//and
void MyTest()
{
Dependencies._mock = new BestMockEver();
//test
Dependencies.Cleanup();
}
Run Code Online (Sandbox Code Playgroud)
我们选择了这条路线,因为这是一个庞大的构建工程项目,将这些服务注入到需要它们的每个类中.同时,这些是我们的代码库中的大多数功能所依赖的通用服务.
我理解这种模式在隐藏依赖关系的意义上是不好的,而不是构造函数注入使得依赖关系显式化.
然而,好处是:
- 我们可以立即开始单元测试,而不是进行3个月的重构,然后进行单元测试.
- 我们仍然有全局变量,但这似乎比我们现在更好.
虽然我们的依赖关系仍然是隐含的,但我认为这种方法比我们的方法更好.除了隐藏的依赖关系,这在某种程度上比使用适当的DI容器更糟糕吗?我会遇到什么问题?
它的服务定位器很糟糕。但你已经知道了。如果您的代码库如此庞大,为什么不开始部分迁移呢?向容器注册单例实例,并在您触摸代码中的类时启动构造函数注入它们。然后,您可以让大部分部件处于(希望如此)工作状态,并在其他地方获得 DI 的好处。
理想情况下,没有 DI 的零件应该随着时间的推移而收缩。您可以立即开始测试。