将测试驱动的开发引入遗留代码

时间:2013-10-25 11:56:59

标签: java oop tdd design-principles

鉴于:LegacyControllerClass扩展了MonsterFrameworkClass(人们刚刚生活多年的一个非常令人讨厌的框架的一部分)。 Framework类具有很多魔力,从默认构造函数中的大量逻辑到反射加载类的静态块。

LegacyControllerClass中的许多生命周期方法,它们会改变全局状态。 execute()方法是一个千言万语,具有你能想到的所有邪恶。

public class LegacyControllerClass extends MonsterFrameworkClass {

   //Life-cycle method
   public void validate(){...}

   //Life-cycle method
   public void preExecute() {...}

   //Life-cycle method
   public void execute() {
       //oodles of legacy code here:
       //    examples: call static methods of 'Util' classes
       //              lots of new X().y(..)
       //              call databases, services
       //              Even call super.execute()!!
       //              More rubbish
       //              More rubbish 
   }
}

好的,现在行动的场景是execute()方法。我正在通过测试驱动一个他们称之为“故事”的项目来向这些穷人介绍测试驱动开发。 “故事”涉及向responseProperties添加错误消息,以便视图(jsp)可以将其读取并显示。伪代码类似于:

if (error) {
    Build the error message
    Add the error message into the responseProperties
}

遗憾的是,这段代码必须在execute()方法中的垃圾之间进行混淆。

我的问题是:我能做的最好的事情是什么? 我能提出的解决方案是:

  1. 提取两种方法rubbish1()rubbish2()
  2. 在我的测试代码中将它们存在于任何期望中(例如; - 设置错误标志)
  3. 将我的代码放在rubbish1()rubbish2()
  4. 之间

    我开始以这种方式实现它,但是MonsterFrameworkClass实际上正在妨碍我:像静态加载,加载ResourceBundle随机属性文件的构造函数魔法等等。

    有没有其他方法来处理相同的事情?

    小小的免责声明:我肯定会购买迈克尔·费瑟的“使用传统代码”并将其吞下去,但这似乎是一个悬而未决的成果。

1 个答案:

答案 0 :(得分:1)

垃圾重构为方法很好。现在你可以下载Powermock来模拟代码中的所有可怕的垃圾。完成模拟后,您可以测试代码。

如果你不为这个怪物添加任何东西,那就更好了。您可以通过为新内容编写自己的类来将您的功能组合成MonstrosityRubbish(或其他)。

如果可能的话,最重要的是不要触及任何遗留代码,您可以将代码组合到其中。

所以在代码中:

private MyShinyClass yourShinyObject; // init

public void execute(Param param0) {
    rubbish1();
    yourShinyObject.handleError(param0);
    rubbish2();
}

public class MyShinyClass {

    public void handleError(Param param0) {
        // your code here
    }
}

如果你能设法编写这样的新东西,你将只依赖于Param可以被嘲笑/存根,你的代码可以在不设置头发的情况下进行测试。

如果你能以功能性方式编写它,那就更好了,这样你就不必在分离的代码中操作responseProperties

public void execute(Param param0) {
    rubbish1();
    responseProperties.add(yourShinyObject.fetchErrors(param0));
    rubbish2();
}

public class MyShinyClass {

    public List<HopelessError> fetchErrors(Param param0) {
        // your code here
    }
}

当然execute()不需要您可以传递任何您想要{/ 1}}的变量/字段的参数。