在方法中实现操作时,我尝试将其分解为更小的聚焦方法。无论是否有意,我通常最终会使用一个或多个类似控制器或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的实例什么不是,并设置对象图。
当我在同一个类中提升方法链的层次结构时,单元测试会呈指数级混乱,乏味且无趣。
我只是想知道设计本身是否不可测试?
请您详细了解如何测试涉及控制器方法的某些场景。我应该重新审视设计还是以不同的方式进行测试?
答案 0 :(得分:3)
如果这些受保护方法中的每一个都包含大量逻辑,则被测试的类可能具有太多责任(请参阅Single Responsibility Principle)。
您可以将每个重要方法的行为提取到自己的类中,甚至可以将所有方法提取到单个类中。然后可以将这些类注入到测试类中并进行模拟,以便您可以单独测试performA的功能。
作为额外的奖励,您可以简单地相互隔离地测试新提取的类。
这将导致更加面向对象和可测试的设计。
答案 1 :(得分:0)
您需要选项来提高代码的可测试性:
为了轻松测试代码,应该将其编写为可测试的。 如果代码有以下代码,请检查您的代码,因为它是开发人员编写可测试代码的响应能力。有一篇好文章Guide: Writing Testable Code(pdf book)突出了常见的代码气味(缺陷):
您可以在Michael Feathers的书"Working Effectively with Legacy Code"中找到更多信息。
您的控制器可能有太多责任,可以执行extract class refactoring 以降低单元测试的复杂性。
如果所有子步骤都无法提取到子类中并形成您的班级OperationAPerformer
的单一响应能力,那么最好在{{strong> class under test
中引入您的创建{1}} / TestInitialize()
方法**。由于它是在每次测试之前调用的,因此仅编写一次此代码就足够了。尽管如此,应该在每个测试中编写特定于测试的设置。