参见原文:http://junit.sourceforge.net/doc/cookstour/cookstour.htm
我在这里写一点学习心得,这篇不是翻译。
JUnit是一个设计良好的测试框架,学习的过程让我再次感受到模式所带来的好处。
Command
JUnit适合给开发人员做单元测试(白盒子),这也是JUnit的目标。一个单元测试用例可以代表一个测试步骤,具有原子性的特点。在开发过程中,我们常常把这样的用例封装成一个类,实例化后就代表了一个单元实体。这样做的好处就是让一个测试用例独立出来而不受其他逻辑干扰(我发现这样的想法在现实生活中也是非常合理的)。
Command模式很好的解决了这个问题。对于一个测试用例来说,它可以看成是一个执行体(说白了就是做事步骤)。Command建议将这样具有多个而类似的执行体封装执行类,并遵守一定的规范(Interface)。看下图:
TestCase代表一个Command模板,它被设计成abstract,需要在实际运用中继承它,实现run()。
Template Mothod
有了TestCase还不行,从源码来看,执行测试的逻辑交给run()是远远不够的,因为实际情况复杂的多。在实践过程中,我认为这样一个测试用例是比较合理的:建立一个测试对象->测试它->清除这个测试对象。这三个步骤应该是连续的(它应该理解为一个测试用例而不是三个)。对大多数测试来说这个用例模板是适用的(哦,终于可以用模板这个词了)。
扯了半天终于可以把Template Method模式带出来了,先来看看它的简介:
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
在这个模式中,run()定义了算法(如下面代码),而具体的算法实现则交给子类处理。这样做的好处是,你在写测试用例的时候不必再关注框架的算法了,只需要关注测试内容。
Collecting Parameter
Collecting Parameter不是GoF所涉及的模式类型。它的作用呢,还是先看看原文好了:
It suggests that when you need to collect results over several methods, you should add a parameter to the method and pass an object that will collect the results for you.
为了收集测试结果(别告诉我不需要啊),定义一个收集器当作参数传给run(),看看源码:
TestResult就充当一个测试结果收集器。
我觉得这样做有如下一些好处:
- 隐藏了测试收集的逻辑:对于开发者来说,测试逻辑才是主题。
- 如果TestResult设计成单例,则能将测试结果集中处理,最大限度的重用代码。
(忙活了两天,差点忘了。今天是圣诞)
Adapter And Pluggable Selector
回到先前的Command模式。每个Command代表一个TestCase类,而实际情况是,每个TestCase会有多个测试方法(想象一下,如果一些测试方法使用的是同一个测试对象,那么些多个TestCase就显得很浪费了)。测试的某一时间段内,我们只用一个测试方法。JUnit提供了两个模式来实现--Adapter And Pluggable Selector(我一直觉得这两个方法差不多)。下面介绍Pluggable Selector,Pluggable Selector就是在实例化的时候给定一个参数,比如某个测试方法的字符串名称,这样在实例化TestCase的时候就知道要运行什么测试方法了(用反射就能做到)。看代码
这个模式跟Adapter非常的相似。每次测试的时候,只要指定参数,就能实例化出不同特性的TestCase。
Composite
下面就该看到有多个TestCase的情况了。对于这种情况,使用者希望有跟TestCase一致的处理方式。该Composite登场啦。比较简单,就不啰嗦了。