Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在没有DI的情况下在紧密耦合的业务\数据层上启用单元测试?_C#_Unit Testing_Dependency Injection - Fatal编程技术网

C# 如何在没有DI的情况下在紧密耦合的业务\数据层上启用单元测试?

C# 如何在没有DI的情况下在紧密耦合的业务\数据层上启用单元测试?,c#,unit-testing,dependency-injection,C#,Unit Testing,Dependency Injection,我正在做一个旧的3层设计的项目,任何新增的功能都需要单元测试 问题在于业务层/数据层是紧密耦合的,如下面的示例所示。BL刚刚更新了一个数据层对象。。。所以用这种方式模拟几乎是不可能的。我们没有实现任何依赖项注入,因此构造函数注入是不可能的。那么,修改结构的最佳方式是什么,这样就可以在不使用DI的情况下模拟数据层 public class BLLayer() { public GetBLObject(string params) { using(DLayer dl = ne

我正在做一个旧的3层设计的项目,任何新增的功能都需要单元测试

问题在于业务层/数据层是紧密耦合的,如下面的示例所示。BL刚刚更新了一个数据层对象。。。所以用这种方式模拟几乎是不可能的。我们没有实现任何依赖项注入,因此构造函数注入是不可能的。那么,修改结构的最佳方式是什么,这样就可以在不使用DI的情况下模拟数据层

public class BLLayer()
{

   public GetBLObject(string params)
   {
     using(DLayer dl = new DLayer())
     {  
        DataSet ds = dl.GetData(params);

        BL logic here....

     }
   }
}

您并没有排除构造函数注入本身,只是没有设置IOC容器。没关系,你不需要。你可以做穷人的依赖注入,但仍然保持构造函数注入

使用接口包装数据层,然后创建一个工厂,该工厂将在命令上生成
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....    
    }
  }
}

不要忘记在实际代码中使用实际工厂的默认值。

如果
DLayer
仅在
getbloobject
方法中使用,我将在方法调用中注入工厂。类似:(以@PaulPhillips为例)

然而,似乎您真正想在业务层使用的是
数据集
。因此,另一种方法是让
GetBLObject
在方法调用中获取
DataSet
,而不是
string param
。为了实现这一点,您可以创建一个类来处理从
DLayer
获取
DataSet
。例如:

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解决方案中都必须这样做)可能非常麻烦,因此测试这一点是可能的,但不一定会有很多乐趣

依赖注入只是“控制反转”概念下的许多模式之一。主要标准是在组件之间提供“接缝”,以便您可以分离。简言之,剥猫皮的方法不止一种

依赖项注入本身有几个派生:构造函数注入(通过构造函数传递的依赖项)、属性注入(表示为读/写属性的依赖项)和方法注入(将依赖项传递到方法)。这些模式假定类是“为修改而关闭”的,并将它们的依赖关系公开给消费者进行更改。遗留代码很少以这种方式设计,并且系统范围的体系结构更改(例如转移到构造函数注入和IoC容器)并不总是直接进行的

其他模式包括将对象的分辨率和/或构造与测试对象分离。像工厂一样简单的四人组模式可以创造奇迹。服务定位器就像一个全局对象工厂,虽然我不太喜欢这种模式,但它可以用来解耦依赖关系

在上面概述的示例中,测试模式“要测试的子类”允许您在不进行系统范围的重新架构的情况下引入Seam。在该模式中,将对象创建调用(如“new DLayer())移动到虚拟方法中,然后创建主题的子类


Micheal Feather的“使用遗留代码”有一个模式和技术目录,您可以使用这些模式和技术将遗留代码置于允许您向DI迈进的状态

你所说的“依赖注入的实现使得构造函数注入不可能”到底是什么意思?您的意思是不能在类中使用构造函数,还是没有DI容器/框架?@steenhulthin我们没有在项目中启动并运行DI框架。
dl
在以后的代码中是如何使用的(dl.GetData(params)调用只是将数据检索到dl对象中吗?@steenhulthin是,我已经更新了这个示例,显示了一个有趣的问题:创建一个伪造的代码>数据集< /代码>,我没有考虑到这一点。
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.
    }
}