Apache Cactus-容器内单元测试

作者:聂勇 欢迎转载,请保留作者信息并说明文章来源!

预备

  • jakarta-cactus-1.8.0
  • junit-3.8.2.jar

配置

图1-Cactus手动配置示意图

1、从 http://jakarta.apache.org/cactus/ 下载jakarta-cactus-1.8.0-bin.zip,解压后将lib目录下面的所有*.jar文件复制到应用的WEB-INF/lib目录下。jar文件清单如下:

  • aspectjrt-1.5.3.jar
  • cactus.core.framework.uberjar.javaEE.14-1.8.0.jar
  • cactus.integration.ant-1.8.0.jar
  • cactus.integration.shared.api-1.8.0.jar
  • cargo-ant-0.9.jar
  • cargo-core-uberjar-0.9.jar
  • commons-httpclient-3.1.jar
  • commons-logging-1.1.jar
  • httpunit-1.6.jar
  • jasper-compiler-5.5.9.jar
  • jasper-runtime-5.5.9.jar
  • junit-3.8.2.jar
  • list.txt
  • nekohtml-1.9.6.jar
  • org.mortbay.jetty-5.1.9.jar
  • servlet-api-2.4.jar

2、在WEB-INF/classes目录下建立cactus.properties文件,文件内容示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 必须的属性
cactus.contextURL=http://localhost:8080/test
# 可选属性。默认为 FilterRedirector
#cactus.filterRedirectorName=FilterRedirector
# 可选属性。默认为 ServletRedirector
#cactus.servletRedirectorName = ServletRedirector
# 可选属性。默认为 JspRedirector
#cactus.jspRedirectorName = JspRedirector
# 可选属性。不过,在测试Filter时是必须的,否则将无法成功执行测试。
cactus.jetty.resourceDir=D:/Project/test/WebRoot

实践

Servlet测试

被测试类 LoginServlet 源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class LoginServlet extends AbstractHttpServlet {
/**
* Constructor of the object.
*/
public LoginServlet() {
super();
}
/**
* Initialization of the servlet. <br>
*
* @throws ServletException if an error occurs
*/
public void init() throws ServletException {
}
/**
* Destruction of the servlet. <br>
*/
public void destroy() {
super.destroy();
}
/**
* The doGet method of the servlet. <br>
*
* This method is called when a form has its tag value method equals to get.
*
* @param request the request send by the client to the server
* @param response the response send by the server to the client
* @throws ServletException if an error occurred
* @throws IOException if an error occurred
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
/**
* The doPost method of the servlet. <br>
*
* This method is called when a form has its tag value method equals to post.
*
* @param request the request send by the client to the server
* @param response the response send by the server to the client
* @throws ServletException if an error occurred
* @throws IOException if an error occurred
*/
@SuppressWarnings("unchecked")
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String mobileNum = request.getParameter("NUM");
String password = request.getParameter("PASSWORD");
if (StringUtils.isBlank(mobileNum) || StringUtils.isBlank(password)) {
response.sendRedirect("error.jsp");
} else {
super.doService(request, response, mobileNum);
}
}
}

测试类 LoginServletCactusTest 源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class LoginServletCactusTest extends ServletTestCase {
private LoginServlet loginServlet;
/**
* @see junit.framework.TestCase#setUp()
*/
protected void setUp() throws Exception {
super.setUp();
loginServlet = new LoginServlet();
}
/**
* @see junit.framework.TestCase#tearDown()
*/
protected void tearDown() throws Exception {
super.tearDown();
loginServlet = null;
}
public void beginDoGetEmptyNUM(WebRequest request) {
request.addParameter("NUM", "13518900001");
request.addParameter("PASSWORD", "a");
}
public void testDoGetEmptyNUM() throws ServletException, IOException {
loginServlet.doGet(request, response);
}
public void endDoGetEmptyNUM(WebResponse response) {
assertEquals(200, response.getStatusCode());
assertEquals(true, -1 != response.getText().indexOf("<input type=\"hidden\" name=\"NUM\" value=\"13518900001\">"));
assertEquals(true, -1 != response.getText().indexOf("<input type=\"hidden\" name=\"PASSWORD\" value=\"a\">"));
}
}

测试入口类AllServletTest 源代码:

1
2
3
4
5
6
7
8
9
10
11
public class AllServletTest {
public static Test suite() {
TestSuite suite = new TestSuite("All Servlet Test Suite");
// 这里还可以加入更多的被测试类
suite.addTestSuite(LoginServletCactusTest.class);
return new Jetty5xTestSetup(suite);
}
}

在eclipse中执行 Run –> Run As –> Junit Test 即可。(注意:是将AllServletTest当Junit Test运行)

Filter 测试

被测试类 IPFilter 源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class IPFilter extends HttpServlet implements Filter {
private static List<IPRange> ipList;
/**
* @param ipList the ipList to set
*/
public static void setIpList(List<IPRange> ipList) {
IPFilter.ipList = ipList;
}
/**
* IP地址白名单过滤器。
*/
public IPFilter() {
}
/**
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) arg0;
HttpServletResponse response = (HttpServletResponse) arg1;
String clientIp = request.getRemoteAddr();
if (!StringUtils.isBlank(clientIp) && IPFilterHelper.isIPInWhitelist(ipList, clientIp)) {
arg2.doFilter(arg0, arg1);
} else {
response.sendRedirect("error.jsp");
}
}
/**
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
public void init(FilterConfig arg0) throws ServletException {
ipList = ((IPWhiteList) SpringUtils.getBean("IPWhiteList")).getWhiteListIP();
}
}

被测试类 IPFilterCactusTest 源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class IPFilterCactusTest extends FilterTestCase {
private IPFilter iPFilter;
/**
* @see junit.framework.TestCase#setUp()
*/
protected void setUp() throws Exception {
super.setUp();
iPFilter = new IPFilter();
}
/**
* @see junit.framework.TestCase#tearDown()
*/
protected void tearDown() throws Exception {
super.tearDown();
iPFilter = null;
}
public void beginDoFilterNormal(WebRequest request) {
}
/**
* Test method for {@link com.asiainfo.aimc.impp4cmcc.filter.IPFilter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)}.
*/
public void testDoFilterNormal() throws ServletException, IOException {
iPFilter.init(config);
iPFilter.doFilter(request, response, filterChain);
}
public void endDoFilterNormal(WebResponse response) {
System.out.println(response.getText());
}
}

测试入口类 AllFilterTest 源代码:

1
2
3
4
5
6
7
8
9
public class AllFilterTest {
public static Test suite() {
TestSuite suite = new TestSuite("All Filter Test Suite");
suite.addTestSuite(IPFilterCactusTest.class);
return new Jetty5xTestSetup(suite);
}
}

在eclipse中执行 Run –> Run As –> Junit Test 即可。(注意:是将AllFilterTest 当Junit Test运行)

故障处理

故障1

运行Filter的集成测试时出现如下异常信息:

org.apache.cactus.util.ChainedRuntimeException: Failed to get the test results at [http://localhost:8080/test/FilterRedirector]
at org.apache.cactus.internal.client.connector.http.DefaultHttpClient.doTest_aroundBody0(DefaultHttpClient.java:93)
at org.apache.cactus.internal.client.connector.http.DefaultHttpClient.doTest_aroundBody1$advice(DefaultHttpClient.java:307)
at org.apache.cactus.internal.client.connector.http.DefaultHttpClient.doTest(DefaultHttpClient.java:1)
at org.apache.cactus.internal.client.connector.http.HttpProtocolHandler.runWebTest(HttpProtocolHandler.java:164)
at org.apache.cactus.internal.client.connector.http.HttpProtocolHandler.runTest_aroundBody0(HttpProtocolHandler.java:83)
at org.apache.cactus.internal.client.connector.http.HttpProtocolHandler.runTest_aroundBody1$advice(HttpProtocolHandler.java:307)
at org.apache.cactus.internal.client.connector.http.HttpProtocolHandler.runTest(HttpProtocolHandler.java:1)
………………
org.apache.cactus.internal.client.ParsingException: Not a valid response [404 %2Fimpp4cmcc%2FFilterRedirector+Not+Found]
at org.apache.cactus.internal.client.connector.http.DefaultHttpClient.callGetResult(DefaultHttpClient.java:212)
at org.apache.cactus.internal.client.connector.http.DefaultHttpClient.doTest_aroundBody0(DefaultHttpClient.java:88)
………………

解决方法:在cactus.properties 中加入 cactus.jetty.resourceDir 的配置。

故障2:连接被拒绝

1)有可能是firewall阻止了端口的访问;
2)Jetty 没有启动。

故障3 - web.xml解析失败

原有web.xml的配置如下:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
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">
………………
</web-app>

采用Cactus + Jetty5 集成测试时会失败,原因是Jetty5不支持这样的配置,改用如下方式解决:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
………………
</web-app>

参考

http://jakarta.apache.org/cactus/integration/manual/howto_config.html