一.关于Junit
关于为什么junit.jar包不能放到lib/ext目录中:
先谈谈类装载器
java虚拟机和程序都调用ClassLoader类的loadClass的方法来加载。java虚拟机中可有多个类加载器,bootstrap来加载rt.jar(java核心包),ExtClassLoader加载<JAVA_HOME>/jre/lib/ext装载jar包,AppClassLoader加载classpath环境变量指定的路径中的类。ExtClassLoader是AppClassLoader的父类。
再说junit.jar为什么不能放到lib/ext目录下的原因:
现在如果把junit.jar包放到ext目录下,ExtClassLoader类装载器要把junit.jar先装载,junit然后要去调用那个被测试类,然而那个被测试类是放在classpath中,被AppClassLoader装载,因为ExtClassLoader是AppClassLoader的父类,AppClassLoader已经装载了被测试类,ExtClassLoader并不知道APP已经装载了这个类,它还要去装载被测试类,结果找不到这个类。就会抛出异常了。但是如果把被测试类也打包为jar包放到ext目录底下应该就没问题了,但这样做单元测试的时候是不是比较麻烦?
Junit作为一个单元测试工具,在保证代码的质量上,起到比较重要的作用.我们使用的为Junit3.8.1版本,用的时候需要把junit.jar添加到项目编译和运行的classpath中,在最简单的Junit方式中,我们将所有的测试放到TestCase的子类中, 每个测试必须是public,不接受任何参数,返回void,并且以一个"test"开头的方法名(最佳实践,Junit鼓励你这么做).传统情况下,setUp()方法创建一个对象的通用集合并将其初始化,这些对象在所有的测试中都将被用到.setUp()与构造方法的区别就是:setUp()在每次测试前被调用.测试完之后,再调用tearDown()方法(可选)在每次测试后执行清除工作.
使用Junit的好处:
1、对于xp编程而言,要求在编写代码之前先写测试,这样可以强制你在写代码之前好好的思考代码(方法)的功能和逻辑,否则编写的代码很不稳定,那么你需要同时维护测试代码和实际代码,这个工作量就会大大增加。因此在xp编程中,基本过程是这样的:构思-》编写测试代码-》编写代码-》测试,而且编写测试和编写代码都是增量式的,写一点测一点,在编写以后的代码中如果发现问题可以较块的追踪到问题的原因,减小回归错误的纠错难度
2、对于重构而言,其好处和xp编程中是类似的,因为重构也是要求改一点测一点,减少回归错误造成的时间消耗。
3、对于非以上两种情况,我们在开发的时候使用junit写一些适当的测试也是有必要的,因为一般我们也是需要编写测试的代码的,可能原来不是使用的junit,如果使用junit,而且针对接口(方法)编写测试代码会减少以后的维护工作,例如以后对方法内部的修改(这个就是相当于重构的工作了)。另外就是因为junit有断言功能,如果测试结果不通过会告诉我们那个测试不通过,为什么,而如果是想以前的一般做法是写一些测试代码看其输出结果,然后再由自己来判断结果使用正确,使用junit的好处就是这个结果是否正确的判断是它来完成的,我们只需要看看它告诉我们结果是否正确就可以了,在一般情况下会大大提高效率。
如何使用JUnit写测试?
最简单的范例如下:
1、创建一个TestCase的子类:
package junitfaq;
import
java
.util.*;
import junit.framework.*;
public class SimpleTest extends TestCase {
public SimpleTest(String name) {
super(name);
}
2、写一个测试方法断言期望的结果:
public void testEmptyCollection() {
Collection collection = new ArrayList();
assertTrue(collection.isEmpty());
}
注意:JUnit推荐的做法是以test作为待测试的方法的开头,这样这些方法可以被自动找到并被测试。
3、写一个suite()方法,它会使用反射动态的创建一个包含所有的testXxxx方法的测试套件:
public static Test suite() {
return new TestSuite(SimpleTest.class);
}
4、写一个main()方法以文本运行器的方式方便的运行测试:
public static void main(String args[]) {
junit.textui.TestRunner.run(suite());
}
}
5、运行测试
我的例子
被测试类:
package cn.itcast;
public class FindChar{
int search(char[] chars,char ch) {
//throw new UnsupportedOperationException("");
int i=0;
int index=0;
boolean b=false;
if(chars==null){
throw new IllegalArgumentException();
}
for(;i<chars.length;i++){
if(chars[i]==ch){
// index=i;
b=true;
break;
}
else{
index=-1;
b=false;
}
}
if(b=true){
return index;
}
else
return index;
}
}
测试用例:
package cn.itcast;
import junit.framework.*;
public class TestFindChar extends TestCase{
private FindChar fc=null;
private char[] chars={'a','b','c'};
public void setUp(){
fc=new FindChar();
}
public void testSearchFound(){
int index=fc.search(chars,'c');
assertEquals(index,2);
}
public void testSearchNotFound(){
int index=fc.search(chars,'e');
assertTrue(index==-1);
}
public void testSearchIllegalArguments(){
char[] chs=null;
fc.search(chs,'a');
fail("this is failure");
}
}
打开命令行窗口,set好classpath之后,把两个类都编译之后,开始测试,
输入:java junit.swingui.TestRunner TestFindChar
测试结果:
我们会看到一个err,和一个fail...通过一个...然后我们再去修改被测试类,之后的测试结果为:
看到了吗?红条变为绿条,表示测试全部通过....
NOTICE:error和fail的区别为:error是测试之外的错误,是无法预料的.而fail表示测试失败...你就需要去找被测试类中方法的错了....
二.Junit联手Ant
ant 我们已经很熟悉了.直接把buile.xml配置好,值得学习的是build中关于使用junit和junitreport这两个任务标签.
<?xml version="1.0" encoding="GB2312"
?> <!--encoding属性解决了注释中文问题-->
<project name="junitlesson" default="report">
<property file="lesson6.property"/>
<target name="init">
<mkdir dir="${classes.java.dir}"/>
<mkdir dir="${classes.test.dir}"/>
</target>
<target name="compilejava" depends="init">
<javac srcdir="${src.java.dir}/cn/itcast" destdir="${classes.java.dir}" />
</target>
<target name="compiletest" depends="init">
<javac srcdir="${src.test.dir}/cn/itcast" destdir="${classes.test.dir}" >
<classpath>
<pathelement location="${classes.java.dir}" />
</classpath>
</javac>
</target>
<target name="compile" depends="compilejava,compiletest" />
<target name="clean">
<delete dir="{classes.java.dir}" />
<delete dir="{classes.test.dir}" />
</target>
<target name="test" depends="clean,compile">
<mkdir dir="reports" />
<junit fork="true" printsummary="on" >
<classpath>
<pathelement location="${classes.junit.dir}" />
<pathelement location="${classes.java.dir}" />
<pathelement location="${classes.test.dir}" />
</classpath>
<!--test name="cn.itcast.TestFindChar" todir="." /-->
<!--没有formatter就看不到输出信息,可以有多个formatter如果出错就退出了build(haltonfailure)-->
<batchtest fork="yes" todir="reports"> <!--批处理-->
<fileset dir="${src.test.dir}">
<!--注意include和includes的区别,我在这吃了大亏,找这个错误浪费了我好多时间-->
<!-- batchtest指定的java源文件或class文件,而不是要运行的测试类名称,junit从这些文件中找出要运行的测试类名-->
<include name="**/*Test*.java"/>
<exclude name="**/AllTests.java"/>
</fileset>
</batchtest>
<formatter type="brief" usefile="false"/> <!--如果没有设置formatter,将无法打印出详细的信息-->
<formatter type="plain" /><!--默认usefile为true,将会自动创建txt文件-->
<formatter type="xml" />
</junit>
</target>
<target name="report" depends="test">
<junitreport todir="." >
<fileset dir="./reports">
<include name="TEST-*.xml" /> <!--根据指定文件夹中的.xml文件生成html格式的测试report-->
</fileset>
<report format="frames" todir="." />
</junitreport>
</target>
</project>
build中用到的.property文件:
src.java.dir=src/java
src.test.dir=src/test
classes.java.dir=classes/java
classes.test.dir=classes/test
class1=junit.swingui.TestRunner
classes.junit.dir=D:/junit3.8.1/junit.jar
看到没有,在build中配置的是junit.swingui.TestRunner,程序运行之后的测试结果应该显示在图形界面中,但是,ant运行以后,是以文字信息在命令行窗口中输出的,至于原因可能是
类加载器
的问题.
ant完成之后生成的.html格式的report,如下图:
这样就可以根据测试结果来完善我们的程序了,用起来也很方便的.
三.总结
在使用ant进行junit测试的时候,我们应该注意的是,在src目录下,创建java和test两个文件夹,java中放被测试类,test中放测试类,但是它们的包名是相同的,这就叫分离但等同.
还应该注意的是,classpath应该设置正确,还有脑子要清晰,否则会因为包名问题浪费过多的时间.
在极限编程(XP)中,TDD(Test Driven Development)是一种实践.测试驱动开发.我们应该养成这种测试先行的行为习惯,
注意事项:
1.将测试类和被测试类放在同一个目录里(即同包之内)将会是很方便的.
2.命名规则:一般被测试类写作XXXXTest,而其中的方法写作testXXX