对遗留代码的解依赖技术

  • 参数适配
    • 使用场景:当无法对一个参数的类型使用接口提取,或者该参数难以被"伪装"时.
    • 例如,该参数的类型是一个含有很多方法的接口类型.在进行单元测试时必须编写一个实现该接口的实现类.
    • 可以使用Mock.
    • 问题:从维护的角度来看,传递了一个宽接口,而其实方法内部只使用了该接口的部分契约.
    • 所以,应该尽量使用窄接口来替代宽接口.
  • 分解出方法对象
    • 使用场景:若方法规模太大,或者使用了实例变量或者其他方法.
    • 将大方法移到一个新类中,因为该类仅含有一个方法成员,所以称为方法对象.
    • 可以方便地对方法对象进行测试.
    • 旧方法中的局部变量,可以作为方法对象中的成员变量.
  • 封装全局引用
    • 使用场景:需要对依赖于全局变量的代码进行测试.
    • 目的:让所依赖的全局变量在测试期间具有另一种行为.
    • 方式:使用链接器,连接到另一个全局变量的定义上.对它进行封装,进而解耦.
    • 方法:建立一个类来持有被依赖的全局变量.
  • 暴露静态方法
    • 方式:对于没有使用实例变量的方法,可以直接将其修改为static,这样方便进行测试(不用new类的实例).
    • 实际上,静态数据和成员方法都不属于所在类,应该算是"临时场地".
  • 提取并重写调用
    • 使用场景:解开对全局变量和static方法的依赖,且对一个全局对象没有太多位于不同位置的调用.
    • 步骤:
      • 确定要提取的调用,找出其方法签名.
      • 使用该方法签名创建一个新的方法.
      • 把对目标方法(要提取的)的调用复制到新方法中.
      • 然后在调用的地方,修改为调用该新方法.
  • 提取并重写工厂方法
    • 使用场景:构造函数中对其成员变量的初始化工作可能会造成难以测试.
    • 前提:必须支持构造函数中的虚方法的多态.
    • 步骤:
      • 找出构造函数中初始化成员变量的代码.
      • 将这些代码都转移到一个工厂方法中,且该方法为virtual.
      • 创建一个继承自被测试类的新类,并重写工厂方法.
  • 提取并重写获取方法
    • 使用场景:类似于工厂方法的场景,同时,构造函数中仅构造成员变量,但是并不使用它.
    • 步骤:
      • 找出需要引入获取方法的对象.
      • 将创建该对象所需的所有逻辑提取到一个获取方法中.
      • 将所有使用该对象的代码都替换为通过该获取方法.并在构造函数中将该对象的引用置为null.
      • 在获取方法中加入"首次调用时创建"功能.
      • 子类化该类,重写该获取方法,并在其中提供Fake对象.
  • 引入委托实例
    • 使用场景:如果静态方法中包含了不想在测试时依赖的环境.静态粘着.
    • 步骤:
      • 找出会在测试中造成问题的静态方法.
      • 在其所属类中新建一个实例方法(保持静态方法的签名).在该实例方法内,直接调用静态方法.
      • 找出纳入测试的类中,那些地方使用了该静态方法.将其修改为对新的实例方法的调用.
  • 连接替换
    • 创建一个哑元库,该库内包含跟目标函数集合拥有完全一样签名的函数声明.
    • 然后在测试环境中,使用该哑元库来替代真实的类库.
posted @ 2014-06-19 10:25  robynhan  阅读(496)  评论(0编辑  收藏  举报