通常,此模式由两个或多个类组成,一个是提供模板方法(非抽象)的抽象类,该模板方法具有对由一个或多个具体子类实现的抽象方法的调用。
模板抽象类和具体实现通常位于同一项目中,但是根据项目范围,这些具体对象将被实现到另一个项目中。
在这篇文章中,我们将看到在外部项目上实现具体类时如何测试模板方法模式,或更一般的如何测试抽象类。
让我们看一个简单的模板方法模式示例。 考虑一个负责接收整数向量并计算欧几里得范数的类。 这些整数可以从多个来源接收,并留给每个项目以提供一种获取它们的方法。
模板类如下所示:
public abstract class AbstractCalculator {public double euclideanNorm() {int[] vector = this.read();int total = 0;for(int element:vector) {total+= (element*element); }return Math.sqrt(total);}public abstract int[] read();
}
现在,另一个项目可以扩展上一类,并通过提供read()方法的实现来实现抽象计算器。
public class ConsoleCalculator extends AbstractCalculator {public int[] read() {int [] data = new int[0];Scanner scanner = new Scanner(System.in);//data = read requried data from consolereturn data; }}
编写了具体实现的开发人员将只测试read()方法,他可以“相信”抽象类的开发人员已经测试了非抽象方法。
但是,如果类是抽象的并且需要read()方法的实现,我们将如何在计算方法上编写单元测试?
第一种方法可能是创建伪造的实现:
public class FakeCalculator extends AbstractCalculator {private int[] data;public FakeCalculator(int[] data) {this.data = data;}public int[] read() {return this.data;}}
这不是一个坏方法,但是有一些缺点:
- 测试的可读性较低,读者应该知道这些假类的存在,并且必须确切地知道他们在做什么。
- 作为测试作者,您将花费时间来实现伪类,在这种情况下,这很简单,但是您的项目可能有多个不执行任何抽象类,甚至使用不止一个抽象方法。
- 伪造类的行为是“硬编码的”。
更好的方法是使用Mockito仅模拟抽象方法,同时调用非抽象方法的实现。
public class WhenCalculatingEuclideanNorm {@Testpublic void should_calculate_correctly() {AbstractCalculator abstractCalculator = mock(AbstractCalculator.class, Mockito.CALLS_REAL_METHODS);doReturn(new int[]{2,2}).when(abstractCalculator).read();assertThat(abstractCalculator.euclideanNorm(), is(2.8284271247461903));}@Testpublic void should_calculate_correctly_with_negative_values() {AbstractCalculator abstractCalculator = mock(AbstractCalculator.class, Mockito.CALLS_REAL_METHODS);doReturn(new int[]{-2,-2}).when(abstractCalculator).read();assertThat(abstractCalculator.euclideanNorm(), is(2.8284271247461903));}}
Mockito通过调用真实方法简化了抽象类的测试,并且仅对抽象方法进行了存根处理。 看到这种情况是因为默认情况下我们正在调用真实方法,而不是使用典型的when()then()结构,而是必须使用doReturn模式。
当然,仅当您的项目不包含算法的具体实现,或者您的项目将成为另一个项目的第3方库的一部分时,才可以使用此方法。 在其他情况下,解决问题的最佳方法是测试实现的类。
下载源代码
参考:在One Jar to Rulem All博客上测试了我们的JCG合作伙伴 Alex Soto的抽象类(尤其是模板方法模式) 。
翻译自: https://www.javacodegeeks.com/2012/06/testing-abstract-classes-and-template.html