步驟如下:
[1] 創建Web工程ServletTestDemo
在myeclipse中創建Web project,命名為ServletTestDemo
[2] 下載cactus-bin-1.8
從apache官方網站下載
[3] 導入cactus的相關包到項目的lib目錄
導入后,/WebRoot/WEB-INF/lib目錄結構如下所示:
|-- lib
| |-- 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.4.jar
| |-- commons-httpclient-3.1.jar
| |-- commons-logging-1.1.jar
| |-- httpunit-1.6.jar
| `-- junit-3.8.2.jar
[4] 配置項目web.xml
在項目web.xml中添加如下內容:
<servlet>
<servlet-name>ServletRedirector</servlet-name>
<servlet-class>org.apache.cactus.server.ServletTestRedirector</servlet-class>
<init-param>
<param-name>param1</param-name>
<param-value>value1 used for testing</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>ServletTestRunner</servlet-name> <servlet-class>org.apache.cactus.server.runner.ServletTestRunner</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletRedirector</servlet-name>
<url-pattern>/ServletRedirector</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ServletTestRunner</servlet-name>
<url-pattern>/ServletTestRunner</url-pattern>
</servlet-mapping>
[5] 創建Servlet
創建LoginServlet,代碼如下:
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 = 1L;
public LoginServlet() {
super();
}
public void destroy() {
super.destroy(); // Just puts "destroy" string in log
// Put your code here
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out
.println("<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.01 Transitional//EN"">");
out.println("<HTML>");
out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
out.println(" <BODY>");
out.print(" This is ");
out.print(this.getClass());
out.println(", using the GET method");
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out
.println("<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.01 Transitional//EN"">");
out.println("<HTML>");
out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
out.println(" <BODY>");
out.print(" This is ");
out.print(this.getClass());
out.println(", using the POST method");
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
}
public String getServletInfo() {
return "This is my default servlet created by Eclipse";
}
public void init() throws ServletException {
}
public void saveSession(HttpServletRequest request)
{
String testparam = request.getParameter("testparam");
request.getSession().setAttribute("testAttribute", testparam);
}
}
[6] 修改項目web.xml,添加servlet映射
在web.xml中添加如下內容:
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
[7] 創建Servlet測試類
創建TestLoginServlet,代碼如下:
import java.io.IOException;
import javax.servlet.ServletException;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.apache.cactus.ServletTestCase;
import org.apache.cactus.WebRequest;
import com.meterware.httpunit.WebResponse;
public class TestLoginServlet extends ServletTestCase {
public TestLoginServlet(String theName) {
super(theName);
}
public static Test suite() {
return new TestSuite(TestLoginServlet.class);
}
public void beginSaveSessionOk(WebRequest webRequest) {
webRequest.addParameter("testparam", "it works!");
}
public void testSaveSessionOk() {
LoginServlet servlet = new LoginServlet();
servlet.saveSession(request);
assertEquals("it works!", session.getAttribute("testAttribute"));
}
public void endSaveSessionOk(WebResponse response) {
System.out.println("end save session ok");
}
public void beginDoGet(WebRequest webRequest) {
webRequest.addParameter("testparam", "it works!");
}
public void testDoGet() {
LoginServlet servlet = new LoginServlet();
try {
servlet.doGet(request, response);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
[8] 復制測試結果報表樣式
下載http://jakarta.apache.org/cactus/misc/cactus-report.xsl
復制cactus-report.xsl到項目的根目錄。
在測試結果顯示時需要此樣式。
[9] 驗證目錄結構
執行完以上操作, 目錄結構如下圖:
.
|-- src
| |-- LoginServlet.java
| `-- TestLoginServlet.java
`-- WebRoot
|-- cactus-report.xsl
|-- index.jsp
|-- META-INF
| `-- MANIFEST.MF
`-- WEB-INF
|-- cactus.properties---
|-- classes
| |-- LoginServlet.class
| `-- TestLoginServlet.class
|-- lib
| |-- 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.4.jar
| |-- commons-httpclient-3.1.jar
| |-- commons-logging-1.1.jar
| |-- httpunit-1.6.jar
| `-- junit-3.8.2.jar
`-- web.xml
[10] 啟動服務
[11] 執行測試
在瀏覽器地址欄,輸入:
http://localhost:8080/ServletTestDemo/ServletTestRunner?suite=TestLoginServlet,回車,測試結果如下:
此測試結果是以
如果結構以html報表的形式顯示,可以輸入如下地址:
回車,結果如下:
Designed for use with Cactus. |
Tests | Failures | Errors | Success rate | Time |
---|---|---|---|---|
2 | 0 | 0 | 100.00% | 0.527 |
Note: failures are anticipated and checked for with assertions while errors are unanticipated. |
Name | Status | Type | Time(s) |
---|---|---|---|
testSaveSessionOk | Success | 0.516 | |
testDoGet | Success | 0.010 |
Back to top
[12]
安裝Bugzilla完成后,執行測試
# .testserver.pl http://192.168.5.5/bugzilla時,出現如下錯誤
TEST-WARNING
Webserver is running under group id not matching
$webservergroup.
This if the tests below fail, this is probably
the problem.
Please refer to the web server configuration section
of the Bugzilla guide.
If you are using virtual hosts or suexec,
this warning may not apply.
TEST-OK Got padlock picture.
TEST-OK
Webserver is executing CGIs via mod_cgi.
TEST-OK Webserver is
preventing fetch of http://10.0.1.21:8081/localconfig.
TEST-WARNING
Failed to run gdlib-config; can't compare GD versions.
TEST-OK
GD library generated a good PNG image.
TEST-OK Chart library
generated a good PNG image.
TEST-OK Template::Plugin::GD is
installed.
解決方法:
# aptitude install libgd2-noxpm-dev -y
再測
# .testserver.pl http://192.168.5.5/bugzilla
TEST-OK Got padlock picture.
TEST-OK Webserver is executing CGIs via mod_cgi.
TEST-OK Webserver is preventing fetch of http://10.0.1.21:8081/localconfig.
TEST-OK GD version 2.39, libgd version 2.0.36; Major versions match.
TEST-OK GD library generated a good PNG image.
TEST-OK Chart library generated a good PNG image.
TEST-OK Template::Plugin::GD is installed.
就OK了。
Cactus簡介
. 簡介
Cactus實現了對JUnit測試框架的無縫擴展,可以方便地測試服務端應用程序。Cactus可以在下面幾種情況下使用:
Cactus的使用也是非常簡單的,你寫的測試類只需繼承ServletTestCase或者JspTestCase、FilterTestCase(它們都繼承了JUnit的TestCase)。寫好測試代碼后需要啟動web容器,然后執行測試代碼。在下面的章節中我們將通過例子向你詳細講解。
Cactus項目Apache Jakarta Commons的一個子項目,網址是:http://jakarta.apache.org/commons/cactus/。
. TestCase框架
在Cactus下,我們寫的TestCase與JUnit有所不同,先看一段代碼,如下:
public class TestSample extendsServletTestCase/JspTestCase/FilterTestCase {上面是一個Cactus測試類的完整代碼框架,其中的extends部分需要按你所測試的不同目標來繼承不同的類(簡介中有所描述)。
另外我們注意到兩個新的方法beginXXX和endXXX的,這兩個方法分別會在testXXX執行前和執行后執行,它們和setUp、tearDown不同的是beginXXX和endXXX會在相應的testXXX前執行,而setUp和tearDown則在每個testXXX方法前都會執行。另外beginXXX和endXXX是客戶端代碼,所以在這兩個方法里是無法使用request這樣的服務端對象的。
對于endXXX方法需要另加說明的是,在Cactus v1.1前(包括v1.1),它的形式是這樣的public void endXXX(HttpURLConnection theConnection),而在Cactus v1.2開始它的形式有兩種可能:
可以看到區別在于引用的包不同,為什么會這樣的呢?因為在v1.2開始Cactus集成了HttpUnit這個組件。如果你熟悉HttpUnit這個組件,我想應該明白為什么要集成HttpUnit。下面我們來看一段代碼開比較一下兩者的區別:
public void endXXX(org.apache.cactus.WebResponse theResponse) {
String content = theResponse.getText();
assertEquals(content, "<html><body><h1>Hello world!</h1></body></html>");
}
public void endXXX(com.meterware.httpunit.WebResponse theResponse) {
WebTable table = theResponse.getTables()[0];
assertEquals("rows", 4, table.getRowCount());
assertEquals("columns", 3, table.getColumnCount());
assertEquals("links", 1, table.getTableCell(0, 2).getLinks().length);
}
當然,在實際應用中你需要根據不同的需要來選擇不同的endXXX。兩個WebResponse的差別可以參見兩者各自的API Doc,這里就不再多說了。
如何在Cactus里寫測試
. 寫測試代碼
首先,我們給出被測類的代碼,是一個Servlet:
public class SampleServlet extends HttpServlet {
public void doGet(HttpServletRequest theRequest,
HttpServletResponse theResponse) throws IOException {
PrintWriter pw = theResponse.getWriter();
theResponse.setContentType("text/html");
pw.print("<html><head/><body>");
pw.print("A GET request");
pw.print("</body></html>");
}
public String checkMethod(HttpServletRequest theRequest) {
return theRequest.getMethod();
}
}
Cactus中的測試類框架已經在上面給出。下面來看一下例子,例子是從中Cactus自帶的實例中抽取的一部分,如下:
public class TestSampleServlet extends ServletTestCase {
public void testReadServletOutputStream() throws IOException {
SampleServlet servlet = new SampleServlet();
servlet.doGet(request, response);
}
public void endReadServletOutputStream(WebResponse theResponse)
throws IOException {
String expected = "<html><head/><body>A GET request</body></html>";
String result = theResponse.getText();
assertEquals(expected, result);
}
public void beginPostMethod(WebRequest theRequest) {
theRequest.addParameter("param", "value", WebRequest.POST_METHOD);
}
public void testPostMethod() {
SampleServlet servlet = new SampleServlet();
assertEquals("POST", servlet.checkMethod(request));
assertEquals("value", request.getParameter("param"));
}
}
第一個方法testReadServletOutputStream,調用doGet,相當于在客戶端提交請求,然后在Servlet處理后會產生一個回饋,所以,在endReadServletOutputStream方法里,我們通過調用response的相應方法判斷回饋是否符合預期結果。
第二個方法testPostMethod,在這之前有一個beginPostMethod,在這個方法里我們以POST方式往request里增加一個表單數據param,值為”value”。下面在testPostMethod我們就要驗證表單數據是否以POST方式提交到了服務端的Servlet里,所以,我們看到了兩個assertEquals,分別進行了判斷。在這里我們要注意到beginPostMethod方法中的theRequest和testPostMethod中的request的區別,在前面我們已經提到過,beginPostMethod是在客戶端執行的,所以它方法內的所有操作事實上是模擬頁面操作的,比如上面的設置表單數據,而testPostMethod是服務端執行的,其中的request也是服務端的。
配置cactus.properties和web.xmlcactus.properties
這個屬性是必須的,它指定了web應用的訪問地址
例:cactus.contextURL = http://localhost:8080/test
可選,當測試類繼承ServletTestCase時用于指定Cactus Servlet Redirector的映射名稱。默認:ServletRedirector
例:cactus.servletRedirectorName = ServletRedirector
可選,當測試類繼承ServletTestCase時用于指定Cactus Jsp Redirector的映射名稱。默認:ServletRedirector
例:cactus.jspRedirectorName = JspRedirector
可選,當測試類繼承ServletTestCase時用于指定Cactus Filter Redirector的映射名稱。默認:ServletRedirector
例:cactus.filterRedirectorName = FilterRedirector
Cactus.properties你可以放置在WEB-INF/classes/下。
web.xml
在web.xml里要為相應的測試類指定相應的Cactus Redirector。
ServletTestCase對應org.apache.cactus.server.ServletTestRedirector
JspTestCase對應/jspRedirector.jsp
FilterTestCase對應org.apache.cactus.server.FilterTestRedirector
<web-app>
<filter>
<filter-name>FilterRedirector</filter-name>
<filter-class>org.apache.cactus.server.FilterTestRedirector</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterRedirector</filter-name>
<url-pattern>/FilterRedirector</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>ServletRedirector</servlet-name>
<servlet-class>org.apache.cactus.server.ServletTestRedirector</servlet-class>
</servlet>
<servlet>
<servlet-name>JspRedirector</servlet-name>
<jsp-file>/jspRedirector.jsp</jsp-file>
</servlet>
<servlet-mapping>
<servlet-name>ServletRedirector</servlet-name>
<url-pattern>/ServletRedirector</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>JspRedirector</servlet-name>
<url-pattern>/JspRedirector</url-pattern>
</servlet-mapping>
</web-app>
如果你的測試類繼承了JspTestCase則需要將jspRedirector.jsp文件放置到你在web.xml中指定的路徑里。
安裝說明
如下:
junit.jar
servlet.jar
cactus.jar
httpclient.jar
commons-logging.jar
httpunit.jar,Tidy.jar,xerces.jar(可選,如果你集成了httpunit的話就需要,也就是在endXXX中使用了httpunit)
cactus.jar
junit.jar
aspectjrt.jar
commons-logging.jar
很多時候,我們要寫一些單元測試來測試我們程序是否能正確觸發異常。
比如下面的例子中,我們就寫了一個test case來測試一個Email驗證類EmailAddrValidator,這個類有一個doValidate(email)方法可以驗證email是否合法,如果不合法則會拋出ValidationException異常。因此我們寫了兩個方法來進行單元測試,前一個方法testDoValidate用來測試正常值,后一個方法testDoValidateException用來測試對錯誤的email格式是否能正確觸發異常。
這個例子的關鍵是方法testDoValidateException(String email) 。
import junit.framework.TestCase;
public class TestEmailAddrValidator extends TestCase {
??? EmailAddrValidator validator = new EmailAddrValidator();
??? public void testDoValidate() throws ValidationException {
??????? validator.doValidate("glchengang@163.com", null);
??????? validator.doValidate("glchen.gang@163.com", null);
??????? validator.doValidate("glchen_gang@163.com", null);
??????? validator.doValidate("glchen.gang@163_tom.com", null);
??? }
??? public void testDoValidateException() {
??????? testDoValidateException("@b.c");
??????? testDoValidateException("a@.c");
??????? testDoValidateException("a@b.");
??????? testDoValidateException("@.c");
??????? testDoValidateException("@...");
??????? testDoValidateException(" ");
??????? testDoValidateException(null);
??? }
??? private void testDoValidateException(String email) {
??????? try {
??????????? validator.doValidate(email, null);
??????????? fail("末拋出異常");
??????? } catch (ValidationException e) {
??????????? assertTrue(true);
??????? }
??? }
}
-----------------------------------------
注:在這里Locale 參數并沒有用到。
import java.util.Locale;
import com.hygensoft.common.configure.ConfigureObject;
public class EmailAddrValidator{
??? protected static final String ERROR_CODE_INVALID_EMAIL_ADDR = "INVALID_EMAIL_ADDR";
??? protected static final String ERROR_CODE_INVALID_INPUT = "INVALID_INPUT_OBJECT";
??? public Object doValidate(Object input, Locale locale) throws ValidationException {
??????? if (!(input instanceof String)) {
??????????? throw new ValidationException(ERROR_CODE_INVALID_INPUT, input);
??????? }
??????? String inputStr = (String) input;
??????? int idx = inputStr.indexOf('@');
??????? if (idx == -1 || idx == 0) {
??????????? throw new ValidationException(ERROR_CODE_INVALID_INPUT, input);
??????? }
??????? int idx2 = inputStr.indexOf('.', idx);
??????? if (idx2 == -1 || idx2 == idx + 1) {
??????????? throw new ValidationException(ERROR_CODE_INVALID_INPUT, input);
??????? }
??????? if (inputStr.endsWith(".")) {
??????????? throw new ValidationException(ERROR_CODE_INVALID_INPUT, input);
??????? }
??????? return input;
??? }
??? /* (non-Javadoc)
???? * @see com.hygensoft.common.configure.Configurable#initialize(com.hygensoft.common.configure.ConfigureObject)
???? */
??? public void initialize(ConfigureObject conf) {}
}