我正在开发一个较旧的3层设计项目,添加的任何新功能都需要进行单元测试。
问题是业务层/数据层紧密耦合,如下面的示例所示。 BL只是新闻数据层对象...所以几乎不可能以这种方式进行模拟。我们没有实现任何依赖注入,因此无法进行构造函数注入。那么,修改结构的最佳方法是什么,以便可以在不使用DI的情况下模拟数据层?
public class BLLayer()
{
public GetBLObject(string params)
{
using(DLayer dl = new DLayer())
{
DataSet ds = dl.GetData(params);
BL logic here....
}
}
}
答案 0 :(得分:5)
您不排除构造函数注入本身,您只是没有设置IOC容器。没关系,你不需要。你可以做穷人的依赖注入并仍然保持构造函数注入。
使用接口包装DataLayer,然后创建一个工厂,根据命令生成IDataLayer
个对象。将此字段作为字段添加到您要注入的对象中,将所有new
替换为对工厂的调用。现在你可以注入你的假货进行测试,如下所示:
interface IDataLayer { ... }
interface IDataLayerFactory
{
IDataLayer Create();
}
public class BLLayer()
{
private IDataLayerFactory _factory;
// present a default constructor for your average consumer
ctor() : this(new RealFactoryImpl()) {}
// but also expose an injectable constructor for tests
ctor(IDataLayerFactory factory)
{
_factory = factory;
}
public GetBLObject(string params)
{
using(DLayer dl = _factory.Create()) // replace the "new"
{
//BL logic here....
}
}
}
不要忘记在实际代码中使用实际工厂的默认值。
答案 1 :(得分:3)
依赖注入只是属于“控制反转”这一概念的众多模式之一。主要标准是在组件之间提供“接缝”,以便您可以分开。简而言之,不止一种方法可以给猫皮肤。
依赖注入本身有几个派生:构造函数注入(通过构造函数传入的依赖项),属性注入(表示为读/写属性的依赖项)和方法注入(依赖项传递给方法)。这些模式假设类“关闭以进行修改”并暴露其依赖性以供消费者更改。遗留代码很少以这种方式设计,系统范围的体系结构更改(例如转移到构造函数注入和IoC容器)并不总是直截了当。
其他模式涉及在测试下将对象的分辨率和/或构造与对象分离。像工厂这样的简单四种模式可以创造奇迹。服务定位器就像一个全局对象工厂,虽然我不是这种模式的忠实粉丝,但它可以用来解耦依赖关系。
在上面概述的示例中,测试模式“Subclass to Test”允许您在没有系统范围的重新架构的情况下引入接缝。在模式中,将对象创建调用(如“new DLayer()”)移动到虚拟方法,然后创建主题的子类。
Micheal Feather的“使用传统代码”有一个模式和技术目录,您可以使用这些模式和技术将遗留代码置于允许您转向DI的状态。
答案 2 :(得分:2)
如果DLayer
仅用于GetBLObject
方法,我会在方法调用中注入工厂。类似于:(基于@PaulPhillips示例)
public GetBLObject(string params, IDataLayerFactory dataLayerFactory)
{
using(DLayer dl = dataLayerFactory.Create()) // replace the "new"
{
//BL logic here....
}
}
但是,您真正想要在业务层中使用的内容似乎是DataSet
。另一种方法是让GetBLObject
取代DataSet
方法调用中的string param
。为了完成这项工作,您可以创建一个只处理来自DataSet
的{{1}}的类。例如:
DLayer
一个警告:模拟public class CallingBusinesslayerCode
{
public void CallingBusinessLayer()
{
// It doesn't show from your code what is returned
// so here I assume that it is void.
new BLLayer().GetBLObject(new BreakingDLayerDependency().GetData("param"));
}
}
public class BreakingDLayerDependency
{
public DataSet GetData(string param)
{
using (DLayer dl = new DLayer()) //you can of course still do ctor injection here in stead of the new DLayer()
{
return dl.GetData(param);
}
}
}
public class BLLayer
{
public void GetBLObject(DataSet ds)
{
// Business Logic using ds here.
}
}
(你必须同时使用这个和Paul Phillips解决方案)可能真的很麻烦,所以测试这个是可能的,但不一定很有趣。