1.2 junit单元测试
大部分集成工具都集成了junit单元测试插件,并有向导帮助写单元测试。Junit发行包的文档很详细地介绍了Junit的设计概念和所使用的设计模式。在这里我简单地说明如何写测试用例、在ant配置文件中调用测试用例和产生测试报告的方法。
写测试用例
下面是在eclipse junit向导对MyCode类编写的测试用例TestMyCode文件基础上写的代码:
import junit.framework.TestCase;
/*
* Created on 2003-4-30
*
* To change the template for this generated file go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
/**
* @author gongys
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
public class TestMyCode extends TestCase {
MyCode myFixture=null;
/**
* Constructor for TestTest.
* @param arg0
*/
public TestTest(String arg0) {
super(arg0);
}
/*
* @see TestCase#setUp()
*/
protected void setUp() throws Exception {
super.setUp();
myFixture = new MyCode();
System.out.println("setup");
}
/*
* @see TestCase#tearDown()
*/
protected void tearDown() throws Exception {
super.tearDown();
myFixture = null;
System.out.println("teardown");
}
public void testSetName() {
myFixture.setName("gongys")
assertEquals("gongys", myFixture.getName());
System.out.println("testSetName");
System.out.println(this.getName());
}
public void testSetAge() {
System.out.println("testSetAge");
myFixture.setAge (12)
assertEquals(12,myFixture.getAge());
System.out.println(this.getName());
}
}
有几点需要特殊指出:
一个TestCase子类中可以包含多个test方法,test方法的原型必须是public void testXXX();
在执行过程中,junit框架为每一个test方法实例化一个TestCase子类;
执行testCase的顺序如下:setUp(),testXXX(),teardown();
fixture是指为每一个测试方法准备的东西:比如数据库连接,此时的目标等,一般在setUp()中设置,testXXX()中使用,teardown()中释放。
运行这个测试的结果如下:
setup
testSetName
testSetName
teardown
setup
testSetAge
testSetAge
teardown
ant使用测试用例
利用ant 的junit任务和其子任务test可以在ant配置文件中执行单元测试,如下所示:
<target name="outter_unittest" depends="init">
<junit printsummary="yes" fork="yes" halt >
<classpath>
<fileset dir="${build.dir}">
<include name="TestMyCode.class" />
<include name="MyCode.class" />
</fileset>
<pathelement location="${lib.dir}/${junit.jar}"/>
</classpath>
<formatter type="xml"/>>
<!--this specify the output format of junit -->
<test name="TestMyCode" todir="tempjunit" />>
<!--this will run all testXXX methods of the TestMyCode and generate the output to dir tempjunit , the output file is TEST-TestMyCode .xml -->
</junit>
</target>
需要注意的是:
要正确设置junit任务的classpath子元素,classpath至少要包含三样东西,TestCase子类比如TestMyCode,你测试的代码的java类比如MyCode,和junit.jar;
可以使用formatter子元素设置junit任务中test任务的输出的格式;
test任务可以设置输出文件的名字和目录;
junit任务还有一个子任务batchtest可以用通配符来指定TestCase子类。
Ant中生成测试报告
在上面的一节中我们谈到junit任务可以生成测试结果,并输出到指定的文件和目录中,在ant中,我们还可以用junitreport任务对这些测试结果进行处理,生成html文件:
<junitreport todir="./tempjunit ">
<fileset dir="./tempjunit ">
<include name="TEST-*.xml"/>
</fileset>
<report format="frames" todir="./report/html"/>
</junitreport>
junitreport任务首先把fileset中指定的测试结果归集成一个xml文件,接着用子任务report转化成html文件,子任务report的format属性指定生成的结果是框架的还是没框架的。
1.3 cactus单元测试
cactus单元测试工具是对junit框架的扩充,使junit的思想和便利同样用于Browser/Server web应用程序中的测试,具体的来说就是测试servlet,jsp和filter。 本节讲述cactus 单元测试原理,servlet测试用例的书写(jsp,filter的测试用例的书写请参照cactus文档),如何配置ant运行这样的测试。
cactus 单元测试原理

Cactus提供了好几个扩展JUnit Testcase的子类和相应的redirector,上面的工作原理图解释了cactus测试的工作原理。
其中YYYTestCase = ( ServletTestCase子类 │ FilterTestCase子类 │ JspTestCase 子类)
XXX我们写的testcase名字的后半部分。
下面我们分步骤解释在我们的cactus Testcase子类里头的每一个testXXX()方法的具体情况:
JUnit 测试运行器调用YYYTestCase.runTest()方法。这个方法寻找 beginXXX(WebRequest)方法,如果找到则执行。 传给beginXXX(WebRequest)方法的参数WebRequest 可用来设置 HTTP头, HTTP 参数,这些参数将被发送到第2步的 Redirector 代理。
YYYTestCase.runTest() 方法打开连向Redirector 代理的HTTP 连接,beginXXX(WebRequest)方法设置的HTTP协议参数将被送到代理。
Redirector 代理在服务端作为YYYTestCase的代理(其实我们的YYYTestCase被实例化两次,一次在客户端被JUnit 测试运行器实例化,一次在服务器端被代理实例化,客户端实例执行beginXXX() and endXXX()方法,服务端实例执行Junit 测试用例的方法setup(),testXXX(),and teardown())。Redirector 代理有下列事情可做:
用java的内省功能创建服务端实例;
设置一些缺省对象;
按照客户端实例的意愿创建session。
执行Junit 测试用例的方法setup(),testXXX(),and teardown();
我们的 testXXX()方法调用服务端代码来进行测试,使用assertEquals()方法对测试结果和预期结果进行比较,如果两者相符为测试成功,否则为测试失败;
如果测试失败,Redirector 代理将捕获testXXX()方法抛出的的异常;
Redirector 代理将异常信息返回给客户端的JUnit 测试运行器,JUnit 测试运行器可以生成测试报告;
如果没有异常出现, YYYTestCase.runTest()方法寻找
endXXX(org.apache.cactus.WebResponse) endXXX(com.meterware.httpunit.WebResponse) (后者用在和httpunit集成中) 方法,如果找到则执行。endXXX方法中,我们可以检查返回的HTTP 头, Cookies 和output stream ,这个检查可以借助于Junit的 assertEquals或者cactus提供的帮助类。
在这里需要提出的一点就是:代理不会去真正执行servlet,或filter,或jsp的代码,你需要在testXXX方法中调用或模仿这些代码。
书写servlet测试用例
import java.io.IOException;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.apache.cactus.ServletTestCase;
import org.apache.cactus.WebRequest;
import org.apache.cactus.WebResponse;
public class TestSampleServlet extends ServletTestCase
{
public TestSampleServlet(String theName)
{
super(theName);
}
public static Test suite()
{
return new TestSuite(TestSampleServlet.class);
}
//这个方法在服务端运行,用来设置fixture
public void setup(){
}
//这个方法在服务端运行,用来释放fixture
public void teardown(){
}
//这个方法在客户端运行,可以用来设置请求参数
public void beginSaveToSessionOK(WebRequest webRequest)
{
webRequest.addParameter("testparam", "it works!");
webRequest.setURL("localhost", "test", "SampleServlet" ,"gongys", "name=gongys");
}
//这个方法在服务端运行,用来具体进行代码测试
public void testSaveToSessionOK() throws IOException
{
SampleServlet servlet = new SampleServlet();
servlet.saveToSession(request);
System.out.println(this.request.getPathInfo());
System.out.println(this.request.getParameter("name"));
this.response.getWriter().println("gongys");
assertEquals("it works!", session.getAttribute("testAttribute"));
}
//这个方法在客户端执行,用来验证返回结果
public void endSaveToSessionOK(WebResponse theResponse){
System.out.println(theResponse.getText());
}
}