单元测试控制器方法或模板方法

时间:2014-01-04 23:17:29

标签: unit-testing design-patterns junit tdd bdd

在方法中实现操作时,我尝试将其分解为更小的聚焦方法。无论是否有意,我通常最终会使用一个或多个类似控制器或template-like的方法。

考虑以下示例。

让我们假设整个'A'操作与performA()中的操作(包括其中的子步骤)是属于一个类的一个责任。

public class OperationAPerformer{

    public void performA(A a){
          E1 e1 = performSubstep_b(a.getB1() )
          performSubstep_e(e1, a.getE2() )
    }

    protected E1 performSubstep_b(B1 b1){
         performSubstep_c(b1.getC1(), b1.getC2() )
         performSubstep_d(b1.getD1() )
         .....
         return e1;
    }

    protected void performSubstep_c(C1 c1, C2 c2) {...//simple stuff, no more method invocations}

    protected void performSubstep_d(D1 d1) {...//simple stuff, no more method invocations}

    protected void performSubstep_e(E1 e1, E2 e2) {...//simple stuff, no more method invocations}

}

通过创建C1,C2,D1,E1和E2的文本修复来测试诸如performSubstep_c,performSubstep_d,performSubstep_e之类的方法是相当简单的。

但是,测试控制器类型的方法(如performA或performSubstep_b)变得相对复杂,导致代码混乱和重复。

例如,要测试像performA这样的方法,我将不得不创建一个满足整个调用层次结构的测试夹具,即我需要模拟/存根/伪造A1,B1,C1,C2,D1,E2的实例什么不是,并设置对象图。

当我在同一个类中提升方法链的层次结构时,单元测试会呈指数级混乱,乏味且无趣。

我只是想知道设计本身是否不可测试?

请您详细了解如何测试涉及控制器方法的某些场景。我应该重新审视设计还是以不同的方式进行测试?

2 个答案:

答案 0 :(得分:3)

如果这些受保护方法中的每一个都包含大量逻辑,则被测试的类可能具有太多责任(请参阅Single Responsibility Principle)。

您可以将每个重要方法的行为提取到自己的类中,甚至可以将所有方法提取到单个类中。然后可以将这些类注入到测试类中并进行模拟,以便您可以单独测试performA的功能。

作为额外的奖励,您可以简单地相互隔离地测试新提取的类。

这将导致更加面向对象和可测试的设计。

答案 1 :(得分:0)

您需要选项来提高代码的可测试性

  1. 为了轻松测试代码,应该将其编写为可测试的。 如果代码有以下代码,请检查您的代码,因为它是开发人员编写可测试代码的响应能力。有一篇好文章Guide: Writing Testable Codepdf book)突出了常见的代码气味(缺陷):

    您可以在Michael Feathers的书"Working Effectively with Legacy Code"中找到更多信息。

  2. 您的控制器可能有太多责任,可以执行extract class refactoring 以降低单元测试的复杂性。

  3. 如果所有子步骤都无法提取到子类中并形成您的班级OperationAPerformer的单一响应能力,那么最好在{{strong> class under test中引入您的创建{1}} / TestInitialize()方法**。由于它是在每次测试之前调用的,因此仅编写一次此代码就足够了。尽管如此,应该在每个测试中编写特定于测试的设置。