JUnit是一个开发源代码的Java测试框架用于编写和运行可重复的测试他是用于单元测试框架体系xUnit的一个实例(用于java语言)它包括以下特性 用于测试期望结果的断言(Assertion)
用于共享共同测试数据的测试工具
用于方便的组织和运行测试的测试套件
图形和文本的测试运行器 JUnit最初是由Erich Gamma(GoF之一)和Kent Beck(xp和refactor的先驱之一)编写的
需要说明的是JUnit一般是用来进行单元测试的
因此需要了解被测试代码的内部结构(即所谓的白盒测试)
另外junit是在xp编程和重构(refactor)中被极力推荐使用的工具
因为在实现自动单元测试的情况下可以大大的提高开发的效率
但是实际上编写测试代码也是需要耗费很多的时间和精力的
那么使用这个东东好处到底在哪里呢?笔者认为是这样的
对于xp编程而言
要求在编写代码之前先写测试
这样可以强制你在写代码之前好好的思考代码(方法)的功能和逻辑
否则编写的代码很不稳定
那么你需要同时维护测试代码和实际代码
这个工作量就会大大增加
因此在xp编程中
基本过程是这样的
构思-》编写测试代码-》编写代码-》测试
而且编写测试和编写代码都是增量式的
写一点测一点
在编写以后的代码中如果发现问题可以较块的追蹤到问题的原因
减小回归错误的纠错难度
对于重构而言
其好处和xp编程中是类似的
因为重构也是要求改一点测一点
减少回归错误造成的时间消耗
对于非以上两种情况
我们在开发的时候使用junit写一些适当的测试也是有必要的
因为一般我们也是需要编写测试的代码的
可能原来不是使用的junit
如果使用junit
而且针对接口(方法)编写测试代码会减少以后的维护工作
例如以后对方法内部的修改(这个就是相当于重构的工作了)
另外就是因为junit有断言功能
如果测试结果不通过会告诉我们那个测试不通过
为什么
而如果是想以前的一般做法是写一些测试代码看其输出结果
然后再由自己来判断结果使用正确
使用junit的好处就是这个结果是否正确的判断是它来完成的
我们只需要看看它告诉我们结果是否正确就可以了
在一般情况下会大大提高效率
安装JUnit
安装很简单
先到以下地址下载一个最新的zip包
下载完以后解压缩到你喜欢的目录下假设是JUNIT_HOME然后将JUNIT_HOME下的junitjar包加到你的系统的CLASSPATH环境变量中对于IDE环境对于需要用到的junit的项目增加到lib中其设置不同的IDE有不同的设置这里不多讲
如何使用JUnit写测试?
最简单的范例如下
创建一个TestCase的子类
package junitfaq;
import java
util
*;
import junitframework*; public class SimpleTest extends TestCase { public SimpleTest(String name) {
super(name);
}
写一个测试方法断言期望的结果
public void testEmptyCollection() {
Collection collection = new ArrayList();
assertTrue(collectionisEmpty());
}
注意JUnit推荐的做法是以test作为待测试的方法的开头这样这些方法可以被自动找到并被测试
写一个suite()方法它会使用反射动态的创建一个包含所有的testXxxx方法的测试套件 public static Test suite() {
return new TestSuite(SimpleTestclass);
}
写一个main()方法以文本运行器的方式方便的运行测试
public static void main(String args[]) {
junittextuiTestRunnerrun(suite());
}
}
运行测试
以文本方式运行
java junitfaqSimpleTest
通过的测试结果是
Time:
OK (
tests)
Time上的小点表示测试个数如果测试通过则显示OK否则在小点的后边标上F表示该测试失败
每次的测试结果都应该是OK的这样才能说明测试是成功的如果不成功就要马上根据提示信息进行修正了
如果JUnit报告了测试没有成功它会区分失败(failures)和错误(errors)失败是你的代码中的assert方法失败引起的而错误则是代码异常引起的例如ArrayIndexOutOfBoundsException
以图形方式运行
java junitswinguiTestRunner junitfaqSimpleTest
通过的测试结果在图形界面的绿色条部分
以上是最简单的测试样例
在实际的测试中我们测试某个类的功能是常常需要执行一些共同的操作
完成以后需要销毁所占用的资源(例如网络连接
数据库连接
关闭打开的文件等)
TestCase类给我们提供了setUp方法和tearDown方法
setUp方法的内容在测试你编写的TestCase子类的每个testXxxx方法之前都会运行
而tearDown方法的内容在每个testXxxx方法结束以后都会执行
这个既共享了初始化代码
又消除了各个测试代码之间可能产生的相互影响
JUnit最佳实践
Martin Fowler说过
当你试图打印输出一些信息或调试一个表达式时
写一些测试代码来替代那些传统的方法
一开始
你会发现你总是要创建一些新的Fixture
而且测试似乎使你的编程速度慢了下来
然而不久之后
你会发现你重复使用相同的Fixture
而且新的测试通常只涉及添加一个新的测试方法
你可能会写许多测试代码
但你很快就会发现你设想出的测试只有一小部分是真正有用的
你所需要的测试是那些会失败的测试
即那些你认为不会失败的测试
或你认为应该失败却成功的测试
我们前面提到过测试是一个不会中断的过程
一旦你有了一个测试
你就要一直确保其正常工作
以检验你所加入的新的工作代码
不要每隔几天或最后才运行测试
每天你都应该运行一下测试代码
这种投资很小
但可以确保你得到可以信赖的工作代码
你的返工率降低了
你会有更多的时间编写工作代码
不要认为压力大
就不写测试代码
相反编写测试代码会使你的压力逐渐减轻
应为通过编写测试代码
你对类的行为有了确切的认识
你会更快地编写出有效率地工作代码
下面是一些具体的编写测试代码的技巧或较好的实践方法
不要用TestCase的构造函数初始化Fixture
而要用setUp()和tearDown()方法
不要依赖或假定测试运行的顺序
因为JUnit利用Vector保存测试方法
所以不同的平台会按不同的顺序从Vector中取出测试方法
避免编写有副作用的TestCase
例如
如果随后的测试依赖于某些特定的交易数据
就不要提交交易数据
简单的会滚就可以了
当继承一个测试类时
记得调用父类的setUp()和tearDown()方法
将测试代码和工作代码放在一起
一边同步编译和更新
(使用Ant中有支持junit的task
)
测试类和测试方法应该有一致的命名方案
如在工作类名前加上test从而形成测试类名
确保测试与时间无关
不要依赖使用过期的数据进行测试
导致在随后的维护过程中很难重现测试
如果你编写的软件面向国际市场
编写测试时要考虑国际化的因素
不要仅用母语的Locale进行测试
尽可能地利用JUnit提供地assert/fail方法以及异常处理的方法
可以使代码更为简洁
测试要尽可能地小执行速度快 JUnit和ant结合 ant 提供了两个 target junit 和 junitreport 运行所有 测试用例 并生成 html 格式的报表
具体操作如下 将 junitjar 放在 ANT_HOME\lib 目录下
修改 buildxml 加入如下 内容
<property name=report value=report />
<target name=junitreport depends=clean compile>
<junit printsummary=on fork=true haltonfailure=false failureproperty=testsfailed showoutput=true>
<classpath refid=myclasspath/>
<formatter type=xml/>
<batchtest todir=${report}>
<fileset dir=${build}>
<include name=**/*Test*/>
</fileset>
</batchtest>
</junit>
<junitreport todir=${report}>
<fileset dir=${report}>
<include name=TEST*xml/>
</fileset>
<report format=frames todir=${report}/>
</junitreport>
<fail if=testsfailed> One or more tests failed check the report for detail </fail>
</target>
运行 这个 target
ant 会运行每个 TestCase
在 report 目录下就有了 很多 TEST*
xml 和 一些网页打开 report 目录下的 l 就可以看到很直观的测试运行报告
一目了然