3.4 不愚蠢的子类-再谈TestCase
我们已经应用了命令模式来描述测试。命令模式依赖一个像execute()(在TestCase中叫run())的简单方法来实现。这个简单的接口允许我们通过同一个接口来调用不同的方法实现。
我们需要一个接口来执行测试。无论如何,在同一个测试类中,所有的测试用例均以不同的方法来实现。这样可避免不断增长的类。一个给定的测试用例类可以实现很多不同的方法,每一个方法定义一个简单的测试用例。每一个测试用例通过类似于testMoneyEquals或者testMoneyAdd之类的名称来描述。这些测试用例不需要遵从一致的命令接口。类的不同实例,需要调用不同的方法。因此,我们下一个问题是让所有的测试用例都以同一种面目展示给测试调用者。
回顾可解决该问题的设计模式,适配器模式进入我们的脑中。适配器有以下的定义:“把一个类的接口转换为客户端期望的另一种接口形式”。这听起来很搭配。适配器有多种形式来做到这一点。其中一种方法为使用一个适配器类,它通过子类的方式来适配期望的接口。例如,把testMoneyEquals适配成runTest,我们可实现一个MoneyTest的子类,并且重载runTest去调用testMoneyEquals。
public class TestMoneyEquals extends MoneyTest {
public TestMoneyEquals() { super("testMoneyEquals"); }
protected void runTest () { testMoneyEquals(); }
}
子类的使用需要我们为每一个测试用例都实现一个子类。这样会增添测试者的负担。它违背了JUnit的目标即尽可能简单地去添加一个测试用例。另外,为每一个测试方法都创建一个子类会造成类的泛滥,许多类只有一个简单的方法并不值得这种资源的浪费,并且很难去给这些类提供有意义的命名。
Java提供了匿名内部类来解决类命名的问题。通过匿名内部类,我们可创建一个不需要类名的适配器:
TestCase test= new MoneyTest("testMoneyEquals ") {
protected void runTest() { testMoneyEquals(); }
};
一种简单的可拔插形式称为插件式选择器。插件式选择器来源于Smalktalk,但不局限于Smalktalk,它也适合于Java。在Java语言中没有方法选择器的概念。但是,Java的反射API可允许我们通过方法名去调用这个方法。在Java中我们可利用这个特性去实现插件式选择器。另外,我们通常不在普通的应用代码使用反射中。在我们的项目中要处理一个基础设施框架,反射正好用的上。
JUnit允许客户端选择使用插件式选择器或者实现一个匿名适配类。这样,我们把插件式选择器作为runTest方法的默认实现。在这个项目中,测试用例的名称等同于测试方法的名称。我们使用反射来调用方法以实现以上。首先,我们需要查询Method对象。一旦我们获取了方法对象,即可传参数来调用它。如果测试方法没有参数,我们可传递一个空参数数组:
protected void runTest() throws Throwable {
Method runMethod= null;
try {
runMethod= getClass().getMethod(fName, new Class[0]);
} catch (NoSuchMethodException e) {
assertTrue("Method \""+fName+"\" not found", false);
}
try {
runMethod.invoke(this, new Class[0]);
}
// catch InvocationTargetException and IllegalAccessException
}
JDK1.1 反射API只允许我们查询public方法。因此你必须定义测试方法为public,否则你将得到一个NoSuchMethodException异常。以下是设计快照,加入了适配器和插件式选择器。
图4 TestCase提供匿名内部类实现的适配器或者插件式选择器
3.5 不用关心一个或者多个--测试套件
我们需要运行多个测试。到目前为止,JUnit可以运行一个简单的测试用例并且通过TestResult来报告结果。我们下一个挑战是让它能运行多个不同的测试。当测试的调用者不必关心是运行一个还是多个测试用例时,这个问题就很容易解决了。一个流行的模式很适合这种情况,它就是组合模式。它的含义为:“将对象以树形结构组织起来,以达到整体与部分的层次结构。使得客户端对单个对象和组合对象的使用具有一致性。”整体和部分的层次结构,是我们感兴趣的关键点。我们需要支持测试套件的套件的套件。
组合模式引进以下的参与者:
元件:声明在我们测试中相互引用的接口。
组合物:实现元件这个接口,并包含测试的集合。
叶子:表现为组合中的一个测试用例,它符合组合物的接口形式。
这个模式告诉我们,需要引用一个抽象类同时为单个和组合对象定义公共的接口。这个类的主要形式为一个接口。在Java中使用组合模式,我们更倾向于定义一个接口,而不是一个抽象类。使用接口可避免JUnit在测试中使用特殊的基类。必须要求的是,所有的测试用例都要符合接口的形式。因此我们引用一个接口:
public interface Test {
public abstract void run(TestResult result);
}
测试用例在组合模式中符合叶子形式,需要实现以上的接口。
接着,我们引进组合物这个参与者。我们命名这个类为TestSuite。一个TestSuite把子测试用例放在一个集合中:
public class TestSuite implements Test {
private Vector fTests= new Vector();
}
run()方法委托给它的子测试用例来实现:
public void run(TestResult result) {
for (Enumeration e= fTests.elements(); e.hasMoreElements(); ) {
Test test= (Test)e.nextElement();
test.run(result);
}
}
图5 使用了组合模式的测试套件
最后,客户端必须能添加测试用例到套件中,通过addTest方法可以做到:
public void addTest(Test test) {
fTests.addElement(test);
}
注意,以上代码只依赖于Test这个接口。一旦TestCase和TestSuite符合Test接口,我们可以递归组合测试套件的套件。所有开发者可创建他们自己的测试套件。我们可以把那些套件组合为一个TestSuite,再运行它们。
以下是创建一个TestSuite的例子:
public static Test suite() {
TestSuite suite= new TestSuite();
suite.addTest(new MoneyTest("testMoneyEquals"));
suite.addTest(new MoneyTest("testSimpleAdd"));
}
这样工作地很好,但它要求我们手动添加所有的测试到一个套件中。早期的JUnit采用者告诉我们,这样很愚蠢。无论何时,你写了一个新的测试用例,你必须记得把它添加到静态的suite()方法中,否则它将不能运行。我们为TestSuite增加了一个更方便的构造函数,它以测试用例类作为参数。目的是为了提取测试方法并且创建一个套件来包含它们。这些测试方法必须遵循一个简单的约定,它们的方法名称要以“test”开头,并且没有参数。这个便利的构造函数使用以上的约定,通过使用反射寻找测试方法来构造这些测试对象。使用这个构造函数的简单代码如下:
public static Test suite() {
return new TestSuite(MoneyTest.class);
}
如果你只想运行这些测试用例中的一部分,原来的方式依然是有用的。
3.6 总结
我们已经在JUnit旅程的结尾部分了。以下的图例展示了使用模式的JUnit设计。
图6 JUnit模式总结
注意,TestCase作为框架的抽象中心,只是使用了4种模式。
这里是浏览所有模式的另一种方式。在这个故事墙中,你能按顺序地看到每个模式所带来的影响的抽象表现。因此,命令模式创建了TestCase类,模板方法模式创建了run方法,等等。(故事墙的表示法是图6去除了所有文字的表示法)。
图7 JUnit模式故事墙
分享到:
相关推荐
这个版本的Junit是在2006年发布的,它在Junit3的基础上进行了许多改进,使得测试代码更加简洁、高效且易于维护。单元测试是一种软件开发实践,它允许开发者独立地对程序的各个小部分,即“单元”,进行验证,确保...
提供的API文档(如javadoc)对每个类、接口和方法进行了详细的解释,帮助开发者理解和使用JUnit4.5的各种功能。 总之,JUnit4.5是Java开发者进行单元测试的重要工具,其丰富的特性和强大的扩展能力使其在测试领域...
相比JUnit3中的继承`TestCase`类的方式,这种方式更加灵活,可以将测试代码与业务逻辑更好地分离。 2. **Parameterized Testing**: JUnit4.5支持参数化测试,通过`@Parameters`注解和`public static Collection[]> ...
单元测试是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件下某个特定函数的行为。其实我们每天都在做单元测试。
本篇将重点介绍如何利用**JUnit 4.5**进行单元测试,以及这个版本的一些特性。 ### JUnit简介 JUnit是一个开源的、基于Java的测试框架,由Ernst Leiss和Kent Beck共同创建,主要用于编写和运行可重复的单元测试。...
使用javadocchm制作的junit4.5帮助文档,方便查询。
JUnit4.5API参考手册 JUnit4.5API参考手册
JUnit是Java编程语言中最常用的单元测试框架之一,它允许开发者编写可重复运行的测试用例,以确保代码的正确性和稳定性。"junit4.4+junit4.5+开源码打包下载"这个标题和描述指向的是JUnit的两个版本——4.4和4.5,...
chm格式的junit API 文档,方便离线使用
《JUnit 4.5 SNAPSHOT-20070720:单元测试框架的里程碑》 JUnit,作为Java编程语言中最广泛使用的单元测试框架,是软件开发过程中的重要工具。这个版本,"junit-4.5-SNAPSHOT-20070720-",是JUnit 4.5的一个快照...
单元测试是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件下某个特定函数的行为。其实我们每天都在做单元测试。
junit测试软件是一单元测试,集成测试工具
在JUnit 3中,编写单元测试需要遵循一些特定的规则,例如测试类必须继承自`TestCase`,并且测试方法必须以`test`开头。下面是一个典型的JUnit 3测试用例: ```java import junit.framework.TestCase; import static...
在 JUnit3 中,编写测试用例时有许多限制,比如测试类必须继承 `junit.framework.TestCase` 类,且测试方法必须以 `test` 开头。这些限制在 JUnit4 中已经被取消,取而代之的是灵活的注解机制。例如: - **继承 ...
单元测试所需的junit jar包,里面包含class jar 和源码jar,JUnit 是JAVA语言事实上的标准测试库。JUnit 4是三年以来最具里程碑意义的一次发布。它的新特性主要是针对JAVA5中的标记(annotation)来简化测试,而不是...
junit-4.5 junit-4.5 junit-4.5 junit-4.5 junit-4.5 junit-4.5
Junit3.8.1 chm英文文档 不错的帮助文档
junit3 junit4 api,单元测试的利器
包含翻译后的API文档:junit-4.11-javadoc-API文档-中文(简体)版.zip; Maven坐标:junit:junit:4.11; 标签:junit、jar包、java、中文文档; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,...
JUnit4基础文档 单元测试是软件测试的一种,旨在检验软件的正确性和可靠性。JUnit是一个流行的单元测试框架,广泛应用于Java开发中。本文档介绍了JUnit4的基础知识,包括单元测试的概念、JUnit4的HelloWorld示例、...