半步多 玄玉的博客

Cactus测试Servlet

2013-07-11
玄玉

简介

容器内测试主要是面向JSP、Servlet、Filter等等,它们依赖于Servlet容器

它们所使用的某些对象都是由容器来产生的,我们自己是无法new出来的

其中比较出名的,比如本文中的Cactus框架

Cactus框架可以模拟JavaEE容器进行测试JSP、Servlet、Filter等等

它本身是基于JUnit3.8的,所以不支持JUnit4.x的注解

它已经很长时间没有更新了,其下载地址为http://archive.apache.org/dist/jakarta/cactus/

但不更新不代表它不好,它还是很好用的,在公司和企业中,它的使用还是很多的

工作流程

Cactus分为ClientSideServerSide

ClientSide中包括beginXxx()和endXxx()方法

ServerSide中包括setUp()和testXxx()和tearDown()等方法

执行顺序为:beginXxx-->setUp-->testXxx-->tearDown-->endXxx-->beginYyy-->setUp-->testYyy-->tearDown-->endYyy

可以粗略的将beginXxx和endXxx方法理解为JUnit4.x中的@BeforeClass@AfterClass,尽管它们之间并啥联系

注意:这里beginXxx和testXxx方法中的Xxx应该是相同的,比如beginLogin()和testLogin()才能对应上

示例代码

公共部分

下面是待测试的LoginServlet.Java

package com.jadyer.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginServlet extends HttpServlet {
    private static final long serialVersionUID = 6655227641354075528L;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter out = response.getWriter();
        out.println("<table><tr><td>username</td><td>password</td></tr></table>");
        out.flush();
        out.close();
    }

    public boolean login(HttpServletRequest request){
        String loginUser = (String)request.getSession().getAttribute("loginUser");
        if(null==loginUser || !"https://jadyer.cn/".equals(loginUser)){
            System.out.println("用户[" + loginUser + "]登录失败");
            return false;
        }
        System.out.println("用户[" + loginUser + "]登录成功");
        return true;
    }
}

下面是使用Cactus编写的位于test SourceFolder下的测试用例LoginServletTest.java

package com.jadyer.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import junit.framework.Assert;
import org.apache.cactus.ServletTestCase;
import org.apache.cactus.WebRequest;
import org.xml.sax.SAXException;
import com.meterware.httpunit.WebResponse;

/**
 * ----------------------------------------------------------------------------------------------------
 * 本类是一个继承了org.apache.cactus.ServletTestCase的类(它内部间接继承了junit.framework.TestCase)
 * 这样此类便自动拥有了ServletAPI引用,比如要使用HttpSession就可以直接session.setAttribute()
 * ----------------------------------------------------------------------------------------------------
 * 它所依赖的基础jar,如下所示
 * aspectjrt-1.5.3.jar
 * cactus.core.framework.uberjar.javaEE.14-1.8.1.jar
 * cactus.integration.ant-1.8.1.jar
 * cactus.integration.shared.api-1.8.1.jar
 * commons-codec-1.6.jar(需单独下载)
 * commons-discovery-0.4.jar
 * commons-httpclient-3.1.jar
 * commons-logging-1.1.jar
 * geronimo-j2ee-management_1.0_spec-1.1.jar
 * httpunit-1.6.jar
 * Tidy.jar或者nekoHTML.jar和xercesMinimal.jar(本例中的endDoGet()方法要用到)
 * ----------------------------------------------------------------------------------------------------
 * Tidy.jar需要单独下载
 * nekoHTML.jar和xercesMinimal.jar均可从下载到的nekohtml-1.9.18.zip找到
 * nekoHTML的下载地址为http://sourceforge.net/projects/nekohtml/files/
 * ----------------------------------------------------------------------------------------------------
 * Created by 玄玉<https://jadyer.cn/> on 2013/07/11 10:49.
 */
public class LoginServletTest extends ServletTestCase {
    private LoginServlet loginServlet;

    //它是在ServerSide执行的
    public void setUp(){
        loginServlet = new LoginServlet();
    }

    //beginXxx()方法是在ClientSide执行的
    //若想在testLogin()中获取到这里request添加的username参数,则该方法就应命名为beginLogin()
    public void beginLogin(WebRequest request){
        request.addParameter("username", "Jadyer");
    }

    //它是在ServerSide执行的
    public void testLogin(){
        //获取GET参数
        Assert.assertEquals(request.getParameter("username"), "jadyer");
        //登录失败
        //session.setAttribute("loginUser", "玄玉<https://jadyer.cn/>");
        Assert.assertFalse(loginServlet.login(request));
        //登录成功
        session.setAttribute("loginUser", "https://jadyer.cn/");
        Assert.assertTrue(loginServlet.login(request));
    }

    //它是在ServerSide执行的
    public void testDoGet() throws ServletException, IOException {
        loginServlet.doGet(request, response);
    }

    //endXxx()方法是在ClientSide执行的,该方法对应testDoGet()
    public void endDoGet(WebResponse resp) throws SAXException {
        //这里使用com.meterware.httpunit.WebResponse,而非org.apache.cactus.WebResponse
        //前者提供了很多增强功能(它要额外借助Tidy.jar或者nekoHTML.jar和xercesMinimal.jar作为辅助包)
        Assert.assertEquals(resp.getTables()[0].getCellAsText(0,0), "username");
        Assert.assertEquals(resp.getTables()[0].getCellAsText(0,1), "password");
    }
}

基于Jetty测试Servlet

除公共部分的两个文件外,只需要写一个JUnit3.8的测试套件,就可以了

Tips:关于JUnit测试套件,可参考我的另一篇博文https://jadyer.cn/2010/11/17/junit-suite-param/

package com.jadyer.servlet;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.apache.cactus.extension.jetty.Jetty5xTestSetup;

/**
 * 我们要在beginXxx之前启动Jetty,而Cactus不支持JUnit4中的@BeforeClass
 * 所以为了实现类似功能,我们就借助JUnit3.8的测试套件,最后测试时直接运行此测试套件即可
 * Created by 玄玉<https://jadyer.cn/> on 2013/07/11 10:49.
 */
public class TestAllUseJetty {
    public static Test suite(){
        //由于使用的是Jetty,所以就不用像基于Tomcat的那样麻烦的配置web.xml和cactus.properties
        //只是要注意:这里的值不能以下划线结尾(端口可任意设定)
        System.setProperty("cactus.contextURL", "http://127.0.0.1:8088/testJettyAndCactus");
        TestSuite suite = new TestSuite();
        suite.addTestSuite(LoginServletTest.class);
        //需要额外引入org.mortbay.jetty-5.1.9.jar(取自cactus-1.8.1-bin.zip)
        //否则会报告java.lang.ClassNotFoundException: org.mortbay.jetty.Server
        return new Jetty5xTestSetup(suite);
    }
}

基于Tomcat测试Servlet

除公共部分的两个文件外,与基于Jetty测试Servlet不同的地方在于:

基于Jetty时只要写一个JUnit3.8测试套件就行了,基于Tomcat则需要配置web.xmlcactus.properties

首先是web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
    <servlet>
        <servlet-name>ServletTestRedirector</servlet-name>
        <servlet-class>org.apache.cactus.server.ServletTestRedirector</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ServletTestRedirector</servlet-name>
        <!-- 这里要求是固定的"/ServletRedirector" -->
        <url-pattern>/ServletRedirector</url-pattern>
    </servlet-mapping>
</web-app>

最后是Cactus运行时要用到的,位于test SourceFolder中的(classpath下面)cactus.properties

#键名固定,键值模式为http://localhost:port/contextRoot/
#是否以斜线结尾均可,但这里的端口要与Tomcat的启动端口一致
cactus.contextURL=http://127.0.0.1:8088/cactus_demo/

Content