======================================================
在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/
======================================================
junit是由 erich gamma 和 kent beck 编写的一个回归测试框架(regression testing framework),供java开发人员编写单元测试之用。
1、概述
junit测试是程序员测试,即所谓白盒测试,因为程序员知道被测试的软件如何(how)完成功能和完成什么样(what)的功能。
junit本质上是一套框架,即开发者制定了一套条条框框,遵循这此条条框框要求编写测试代码,如继承某个类,实现某个接口,就可以用junit进行自动测试了。
由于junit相对独立于所编写的代码,可以测试代码的编写可以先于实现代码的编写,xp 中推崇的 test first design的实现有了现成的手段:用junit写测试代码,写实现代码,运行测试,测试失败,修改实现代码,再运行测试,直到测试成功。以后对代码的修改和优化,运行测试成功,则修改成功。
java 下的 team 开发,采用 cvs(版本控制) + ant(项目管理) + junit(集成测试) 的模式时,通过对ant的配置,可以很简单地实现测试自动化。
对不同性质的被测对象,如class,jsp,servlet,ejb等,junit有不同的使用技巧,以后慢慢地分别讲叙。以下以class测试为例讲解,除非特殊说明。
2、下载安装
去junit主页下载最新版本3.8.1程序包junit-3.8.1.zip
用winzip或unzip将junit-3.8.1.zip解压缩到某一目录名为$junithome
将junit.jar和$junithome/junit加入到classpath中,加入后者只因为测试例程在那个目录下。
注意不要将junit.jar放在jdk的extension目录下
运行命令,结果如右图。
java junit.swingui.testrunner junit.samples.alltests
3、junit架构
下面以money这个类为例进行说明。
public class money {
private int famount;//余额
private string fcurrency;//货币类型
public money(int amount, string currency) {
famount= amount;
fcurrency= currency;
}
public int amount() {
return famount;
}
public string currency() {
return fcurrency;
}
public money add(money m) {//加钱
return new money(amount()+m.amount(), currency());
}
public boolean equals(object anobject) {//判断钱数是否相等
if (anobject instanceof money) {
money amoney= (money)anobject;
return amoney.currency().equals(currency())
&& amount() == amoney.amount();
}
return false;
}
}
junit本身是围绕着两个设计模式来设计的:命令模式和集成模式.
命令模式
利用testcase定义一个子类,在这个子类中生成一个被测试的对象,编写代码检测某个方法被调用后对象的状态与预期的状态是否一致,进而断言程序代码有没有bug。
当这个子类要测试不只一个方法的实现代码时,可以先建立测试基础,让这些测试在同一个基础上运行,一方面可以减少每个测试的初始化,而且可以测试这些不同方法之间的联系。
例如,我们要测试money的add方法,可以如下:
public class moneytest extends testcase { //testcase的子类
public void testadd() { //把测试代码放在testadd中
money m12chf= new money(12, "chf"); //本行和下一行进行一些初始化
money m14chf= new money(14, "chf");
money expected= new money(26, "chf");//预期的结果
money result= m12chf.add(m14chf); //运行被测试的方法
assert.asserttrue(expected.equals(result)); //判断运行结果是否与预期的相同
}
}
如果测试一下equals方法,用类似的代码,如下:
public class moneytest extends testcase { //testcase的子类
public void testequals() { //把测试代码放在testequals中
money m12chf= new money(12, "chf"); //本行和下一行进行一些初始化
money m14chf= new money(14, "chf");
assert.asserttrue(!m12chf.equals(null));//进行不同情况的测试
assert.assertequals(m12chf, m12chf);
assert.assertequals(m12chf, new money(12, "chf")); // (1)
assert.asserttrue(!m12chf.equals(m14chf));
}
}
当要同时进行测试add和equals方法时,可以将它们的各自的初始化工作,合并到一起进行,形成测试基础,用setup初始化,用teardown清除。如下:
public class moneytest extends testcase {//testcase的子类
private money f12chf;//提取公用的对象
private money f14chf;
protected void setup() {//初始化公用对象
f12chf= new money(12, "chf");
f14chf= new money(14, "chf");
}
public void testequals() {//测试equals方法的正确性
assert.asserttrue(!f12chf.equals(null));
assert.assertequals(f12chf, f12chf);
assert.assertequals(f12chf, new money(12, "chf"));
assert.asserttrue(!f12chf.equals(f14chf));
}
public void testsimpleadd() {//测试add方法的正确性
money expected= new money(26, "chf");
money result= f12chf.add(f14chf);
assert.asserttrue(expected.equals(result));
}
}
将以上三个中的任一个testcase子类代码保存到名为moneytest.java的文件里,并在文件首行增加
import junit.framework.*;
,都是可以运行的。关于junit运行的问题很有意思,下面单独说明。
上面为解释概念“测试基础(fixture)”,引入了两个对两个方法的测试。命令模式与集成模式的本质区别是,前者一次只运行一个测试。
集成模式
利用testsuite可以将一个testcase子类中所有test***()方法包含进来一起运行,还可将testsuite子类也包含进来,从而行成了一种等级关系。可以把testsuite视为一个容器,可以盛放testcase中的test***()方法,它自己也可以嵌套。这种体系架构,非常类似于现实中程序一步步开发一步步集成的现况。
对上面的例子,有代码如下:
public class moneytest extends testcase {//testcase的子类
....
public static test suite() {//静态test
testsuite suite= new testsuite();//生成一个testsuite
suite.addtest(new moneytest("testequals")); //加入测试方法
suite.addtest(new moneytest("testsimpleadd"));
return suite;
}
}
从junit2.0开始,有列简捷的方法:
public class moneytest extends testcase {//testcase的子类
....
public static test suite() {静态test
return new testsuite(moneytest.class); //以类为参数
}
}
testsuite见嵌套的例子,在后面应用案例中有。
4、测试代码的运行
先说最常用的集成模式。
测试代码写好以后,可以相应的类中写main方法,用java命令直接运行;也可以不写main方法,用junit提供的运行器运行。junit提供了textui,awtui和swingui三种运行器。
以前面第2步中的alltests运行为例,可有四种:
java junit.textui.testrunner junit.samples.alltests
java junit.awtui.testrunner junit.samples.alltests
java junit.swingui.testrunner junit.samples.alltests
java junit.samples.alltests
main方法中一般也都是简单地用runner调用suite(),当没有main时,testrunner自己以运行的类为参数生成了一个testsuite.
对于命令模式的运行,有两种方法。
静态方法
testcase test= new moneytest("simple add") {
public void runtest() {
testsimpleadd();
}
};
动态方法
testcase test= new moneytest("testsimpleadd");
我试了一下,好象有问题,哪位朋友成功了,请指点我一下。确实可以。
import junit.framework.*;
public class moneytest extends testcase {//testcase的子类
private money f12chf;//提取公用的对象
private money f14chf;
public moneytest(string name){
super(name);
}
protected void setup() {//初始化公用对象
f12chf= new money(12, "chf");
f14chf= new money(14, "chf");
}
public void testequals() {//测试equals方法的正确性
assert.asserttrue(!f12chf.equals(null));
assert.assertequals(f12chf, f12chf);
assert.assertequals(f12chf, new money(12, "chf"));
assert.asserttrue(!f12chf.equals(f14chf));
}
public void testadd() {//测试add方法的正确性
money expected= new money(26, "chf");
money result= f12chf.add(f14chf);
assert.asserttrue(expected.equals(result));
}
// public static void main(string[] args) {
// testcase test=new moneytest("simple add") {
// public void runtest() {
// testadd();
// }
// };
// junit.textui.testrunner.run(test);
// }
public static void main(string[] args) {
testcase test=new moneytest("testadd");
junit.textui.testrunner.run(test);
}
}
再给一个静态方法用集成测试的例子:
public static test suite() {
testsuite suite= new testsuite();
suite.addtest(
new testcar("getwheels") {
protected void runtest() { testgetwheels(); }
}
);
suite.addtest(
new testcar("getseats") {
protected void runtest() { testgetseats(); }
}
);
return suite;
}
5、应用案例
junit primer例程,运行如下:
java com.hedong.junitlearning.primer.shoppingcarttest
ant+junit+mailto实现自动编译、调试并发送结果的build.xml
junit实施,写得很棒,理解也深刻。例程运行如下:
java com.hedong.junitlearning.car.testcarnojunit
java junit.swingui.testrunner com.hedong.junitlearning.car.testcar
junit与log4j结合,阿菜的例程运行:
cd acai
ant junit
6、一些问题
有人在实践基础上总结出一些非常有价值的使用技巧,我没有经过一一“测试”,暂列在此。
不要用testcase的构造函数初始化fixture,而要用setup()和teardown()方法。
不要依赖或假定测试运行的顺序,因为junit利用vector保存测试方法。所以不同的平台会按不同的顺序从vector中取出测试方法。不知3.8中是不是还是如此,不过它提供的例子有一个是指定用vectorsuite的,如果不指定呢?
避免编写有副作用的testcase。例如:如果随后的测试依赖于某些特定的交易数据,就不要提交交易数据。简单的回滚就可以了。
当继承一个测试类时,记得调用父类的setup()和teardown()方法。
将测试代码和工作代码放在一起,一边同步编译和更新。(使用ant中有支持junit的task.)
测试类和测试方法应该有一致的命名方案。如在工作类名前加上test从而形成测试类名。
确保测试与时间无关,不要依赖使用过期的数据进行测试。导致在随后的维护过程中很难重现测试。
如果你编写的软件面向国际市场,编写测试时要考虑国际化的因素。不要仅用母语的locale进行测试。
尽可能地利用junit提供地assert/fail方法以及异常处理的方法,可以使代码更为简洁。
测试要尽可能地小,执行速度快。
把测试程序建立在与被测对象相同的包中
在你的原始代码目录中避免测试码出现,可在一个源码镜像目录中放测试码
在自己的应用程序包中包含一个testsuite测试类
7、相关资源下载
以下jar包,我只是做了打包、编译和调试的工作,供下载学习之用,相关的权利属于原作者。
可运行例程.jar
build.xml
阿菜的例程
junit api 汉译(pdf)
8、未完成的任务
httpunit
cactus
将junit用链接池测试
======================================================
在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/