3

我正在开发一个较旧的 3 层设计项目,添加的任何新功能都必须是可单元测试的。

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

public class BLLayer()
{

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

        BL logic here....

     }
   }
}
4

3 回答 3

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....    
    }
  }
}

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

于 2012-06-01T22:12:05.030 回答
3

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

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

其他模式涉及将物体的分辨率和/或构造与测试对象分离。像工厂这样的简单的四人帮模式可以创造奇迹。服务定位器就像一个全局对象工厂,虽然我不是这种模式的忠实拥护者,但它可以用来解耦依赖关系。

在您上面概述的示例中,测试模式“要测试的子类”将允许您引入接缝而无需系统范围的重新架构。在该模式中,您将对象创建调用(如“new DLayer()”)移至虚拟方法,然后创建主题的子类。

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

于 2012-06-02T14:02:40.717 回答
2

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

public GetBLObject(string params, IDataLayerFactory dataLayerFactory)
   {
       using(DLayer dl = dataLayerFactory.Create())  // replace the "new"
       {  
            //BL logic here....    
       }
   }

但是,您真正想要在业务层中使用的似乎是DataSet. 所以另一种方法是让in 方法调用而GetBLObject不是. 为了完成这项工作,您可以创建一个类来处理从. 例如:DataSetstring paramDataSetDLayer

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.
    }
}

一个警告:模拟DataSet(在此解决方案和 Paul Phillips 解决方案中都必须这样做)可能非常麻烦,因此对此进行测试是可能的,但不一定很有趣。

于 2012-06-02T08:38:01.390 回答