HttpUnit是一個集成測試工具,主要關注Web應用的測試,提供的幫助類讓測試者可以通過Java類和服務器進行交互,並且將服務器端的響應當作文本或者DOM對象進行處理。HttpUnit還提供了一個模擬Servlet容器,讓你可以不需要發佈Servlet,就可以對Servlet的內部代碼進行測試。本文中作者將詳細的介紹如何使用HttpUnit提供的類完成集成測試。
1. 如何使用httpunit處理頁面的內容
- WebConversation類是HttpUnit框架中最重要的類,它用於模擬瀏覽器的行爲
- WebRequest類,模仿客戶請求,通過它可以向服務器發送信息
- WebResponse類,模擬瀏覽器獲取服務器端的響應信息
1.1 獲取指定頁面的內容
1.1.1 直接獲取頁面內容
- System.out.println("直接獲取網頁內容:");
- //建立一個WebConversation實例
- WebConversation wc = new WebConversation();
- //向指定的URL發出請求,獲取響應
- WebResponse wr = wc.getResponse( "http://localhost:6888/HelloWorld.html" );
- //用getText方法獲取相應的全部內容
- //用System.out.println將獲取的內容打印在控制檯上
- System.out.println( wr.getText() );
1.1.2 通過Get方法訪問頁面並且加入參數
- System.out.println("向服務器發送數據,然後獲取網頁內容:");
- //建立一個WebConversation實例
- WebConversation wc = new WebConversation();
- //向指定的URL發出請求
- WebRequest req = new GetMethodWebRequest( "http://localhost:6888/HelloWorld.jsp" );
- //給請求加上參數
- req.setParameter("username","姓名");
- //獲取響應對象
- WebResponse resp = wc.getResponse( req );
- //用getText方法獲取相應的全部內容
- //用System.out.println將獲取的內容打印在控制檯上
- System.out.println( resp.getText() );
1.1.3 通過Post方法訪問頁面並且加入參數
- System.out.println("使用Post方式向服務器發送數據,然後獲取網頁內容:");
- //建立一個WebConversation實例
- WebConversation wc = new WebConversation();
- //向指定的URL發出請求
- WebRequest req = new PostMethodWebRequest( "http://localhost:6888/HelloWorld.jsp" );
- //給請求加上參數
- req.setParameter("username","姓名");
- //獲取響應對象
- WebResponse resp = wc.getResponse( req );
- //用getText方法獲取相應的全部內容
- //用System.out.println將獲取的內容打印在控制檯上
- System.out.println( resp.getText() );
大家關注一下上面代碼中打了下劃線的兩處內容,應該可以看到,使用Get、Post方法訪問頁面的區別就是使用的請求對象不同。
1.2 處理頁面中的鏈接
這裏的演示是找到頁面中的某一個鏈接,然後模擬用戶的單機行爲,獲得它指向文件的內容。比如在我的頁面HelloWorld.html中有一個鏈接,它顯示的內容是TestLink,它指向我另一個頁面TestLink.htm. TestLink.htm裏面只顯示TestLink.html幾個字符。
下面是處理代碼:
- System.out.println("獲取頁面中鏈接指向頁面的內容:");
- //建立一個WebConversation實例
- WebConversation wc = new WebConversation();
- //獲取響應對象
- WebResponse resp = wc.getResponse( "http://localhost:6888/HelloWorld.html" );
- //獲得頁面鏈接對象
- WebLink link = resp.getLinkWith( "TestLink" );
- //模擬用戶單擊事件
- link.click();
- //獲得當前的響應對象
- WebResponse nextLink = wc.getCurrentPage();
- //用getText方法獲取相應的全部內容
- //用System.out.println將獲取的內容打印在控制檯上
- System.out.println( nextLink.getText() );
1.3 處理頁面中的表格
表格是用來控制頁面顯示的常規對象,在HttpUnit中使用數組來處理頁面中的多個表格,你可以用resp.getTables()方法獲取頁面所有的表格對象。他們依照出現在頁面中的順序保存在一個數組裏面。
[注意] Java中數組下標是從0開始的,所以取第一個表格應該是resp.getTables()[0],其他以此類推。
下面的例子演示如何從頁面中取出第一個表格的內容並且將他們循環顯示出來:
- System.out.println("獲取頁面中表格的內容;");
- //建立一個WebConversation實例
- WebConversation wc = new WebConversation();
- //獲取響應對象
- WebResponse resp = wc.getResponse( "http://localhost:6888/HelloWorld.html" );
- //獲得對應的表格對象
- WebTable webTable = resp.getTables()[0];
- //將表格對象的內容傳遞給字符串數組
- String[][] datas = webTable.asText();
- //循環顯示錶格內容
- int i = 0 ,j = 0;
- int m = datas[0].length;
- int n = datas.length;
- while (i
- j=0;
- while(j
- System.out.println("表格中第"+(i+1)+"行第"+
- (j+1)+"列的內容是:"+datas[i][j]);
- ++j;
- }
- ++i;
- }
1.4 處理頁面中的表單
表單是用來接受用戶輸入,也可以向用戶顯示用戶已輸入信息(如需要用戶修改數據時,通常會顯示他以前輸入過的信息),在HttpUnit中使用數組來處理頁面中的多個表單,你可以用resp.getForms()方法獲取頁面所有的表單對象。他們依照出現在頁面中的順序保存在一個數組裏面。
[注意] Java中數組下標是從0開始的,所以取第一個表單應該是resp.getForms()[0],其他以此類推。
下面的例子演示如何從頁面中取出第一個表單的內容並且將他們循環顯示出來:
- System.out.println("獲取頁面中表單的內容:");
- //建立一個WebConversation實例
- WebConversation wc = new WebConversation();
- //獲取響應對象
- WebResponse resp = wc.getResponse( "http://localhost:6888/HelloWorld.html" );
- //獲得對應的表單對象
- WebForm webForm = resp.getForms()[0];
- //獲得表單中所有控件的名字
- String[] pNames = webForm.getParameterNames();
- int i = 0;
- int m = pNames.length;
- //循環顯示錶單中所有控件的內容
- while(i
- System.out.println("第"+(i+1)+"個控件的名字是"+pNames[i]+
- ",裏面的內容是"+webForm.getParameterValue(pNames[i]));
- ++i;
- }
2. 如何使用httpunit進行測試
2.1 對頁面內容進行測試
httpunit中的這部分測試完全採用了JUnit的測試方法,即直接將你期望的結果和頁面中的輸出內容進行比較。不過這裏的測試就簡單多了,只是字符串和字符串的比較。
比如你期望中的頁面顯示是中有一個表格,它是頁面中的第一個表格,而且他的第一行第一列的數據應該是顯示username,那麼你可以使用下面的代碼進行自動化測試:
- System.out.println("獲取頁面中表格的內容並且進行測試:");
- //建立一個WebConversation實例
- WebConversation wc = new WebConversation();
- //獲取響應對象
- WebResponse resp = wc.getResponse( "http://localhost:6888/TableTest.html" );
- //獲得對應的表格對象
- WebTable webTable = resp.getTables()[0];
- //將表格對象的內容傳遞給字符串數組
- String[][] datas = webTable.asText();
- //對錶格內容進行測試
- String expect = "中文";
- Assert.assertEquals(expect,datas[0][0]);
2.2 對Servlet進行測試
除了對頁面內容進行測試外,有時候(比如開發複雜的Servlets的時候),你需要對Servlet本身的代碼塊進行測試,這時候你可以選擇HttpUnit,它可以提供一個模擬的Servlet容器,讓你的Servlet代碼不需要發佈到Servlet容器(如tomcat)就可以直接測試。
2.2.1 原理簡介
使用httpunit測試Servlet時,請創建一個ServletRunner的實例,他負責模擬Servlet容器環境。如果你只是測試一個Servlet,你可以直接使用registerServlet方法註冊這個Servlet,如果需要配置多個Servlet,你可以編寫自己的web.xml,然後在初始化ServletRunner的時候將它的位置作爲參數傳給ServletRunner的構造器。
在測試Servlet時,應該記得使用ServletUnitClient類作爲客戶端,他和前面用過的WebConversation差不多,都繼承自WebClient,所以他們的調用方式基本一致。要注意的差別是,在使用ServletUnitClient時,他會忽略URL中的主機地址信息,而是直接指向他的ServletRunner實現的模擬環境。
2.2.2 簡單測試
本實例只是演示如何簡單的訪問Servlet並且獲取他的輸出信息,例子中的Servlet在接到用戶請求的時候只是返回一串簡單的字符串:Hello World!.
1. Servlet的代碼如下:
- package janier.servlet;
- import java.io.IOException;
- import java.io.PrintWriter;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- /**
- * @author Janier
- *
- */
- public class HelloWorld extends HttpServlet {
- /**
- *
- */
- private static final long serialVersionUID = 7242188527423883719L;
- public void service(HttpServletRequest req, HttpServletResponse resp)
- throws IOException
- {
- PrintWriter out = resp.getWriter();
- //向瀏覽器中寫一個字符串Hello World!
- out.println("Hello World!");
- out.close();
- }
- }
2. 測試的調用代碼如下:
- package test.servlet;
- import janier.servlet.HelloWorld;
- import com.meterware.httpunit.GetMethodWebRequest;
- import com.meterware.httpunit.WebRequest;
- import com.meterware.httpunit.WebResponse;
- import com.meterware.servletunit.ServletRunner;
- import com.meterware.servletunit.ServletUnitClient;
- import junit.framework.TestCase;
- /**
- * @author Janier
- *
- */
- public class HttpUnitTestHelloWorld extends TestCase{
- protected void setUp() throws Exception {
- super.setUp();
- }
- protected void tearDown() throws Exception {
- super.tearDown();
- }
- public void testHelloWorld() {
- try {
- //創建Servlet的運行環境
- ServletRunner sr = new ServletRunner();
- //向環境中註冊Servlet
- sr.registerServlet( "HelloWorld", HelloWorld.class.getName() );
- //創建訪問Servlet的客戶端
- ServletUnitClient sc = sr.newClient();
- //發送請求
- WebRequest request = new GetMethodWebRequest( "http://localhost/HelloWorld" );
- //獲得模擬服務器的信息
- WebResponse response = sc.getResponse( request );
- //將獲得的結果打印到控制檯上
- System.out.println(response.getText());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
2.2 測試Servlet的內部行爲
下面的代碼演示瞭如何使用HttpUnit模擬Servlet容器,並且通過InvocationContext對象,測試Servlet內部行爲的大部分工作,比如控制request、session、response等。
1.測試代碼
- package test.servlet;
- import janier.servlet.HelloWorld;
- import com.meterware.httpunit.GetMethodWebRequest;
- import com.meterware.httpunit.WebRequest;
- import com.meterware.httpunit.WebResponse;
- import com.meterware.servletunit.InvocationContext;
- import com.meterware.servletunit.ServletRunner;
- import com.meterware.servletunit.ServletUnitClient;
- import junit.framework.Assert;
- import junit.framework.TestCase;
- /**
- * @author Janier
- *
- */
- public class HttpUnitTestHelloWorld extends TestCase{
- protected void setUp() throws Exception {
- super.setUp();
- }
- protected void tearDown() throws Exception {
- super.tearDown();
- }
- public void testHelloWorld() {
- try {
- // 創建Servlet的運行環境
- ServletRunner sr = new ServletRunner();
- //向環境中註冊Servlet
- sr.registerServlet( "HelloWorld", HelloWorld.class.getName() );
- //創建訪問Servlet的客戶端
- ServletUnitClient sc = sr.newClient();
- //發送請求
- WebRequest request = new GetMethodWebRequest( "http://localhost/HelloWorld" );
- request.setParameter("username","testuser");
- //獲得該請求的上下文環境
- InvocationContext ic = sc.newInvocation( request );
- //調用Servlet的非服務方法
- HelloWorld is = (HelloWorld)ic.getServlet();
- //測試servlet的某個方法
- Assert.assertTrue(is.authenticate());
- //直接通過上下文獲得request對象
- System.out.println("request中獲取的內容:"+ic.getRequest().getParameter("username"));
- //直接通過上下文獲得response對象,並且向客戶端輸出信息
- ic.getResponse().getWriter().write("haha");
- //直接通過上下文獲得session對象,控制session對象,給session賦值
- ic.getRequest().getSession().setAttribute("username","timeson");
- //獲取session的值
- System.out.println("session中的值:"+ic.getRequest().getSession().getAttribute("username"));
- //使用客戶端獲取返回信息,並且打印出來
- WebResponse response = ic.getServletResponse();
- System.out.println(response.getText());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
2.調用代碼
- package janier.servlet;
- import java.io.IOException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- /**
- * @author Janier
- *
- */
- public class HelloWorld extends HttpServlet {
- public void saveToSession(HttpServletRequest request) {
- request.getSession().setAttribute("testAttribute",request.getParameter("testparam"));
- }
- public void doGet(HttpServletRequest request,
- HttpServletResponse response) throws IOException{
- String username=request.getParameter("username");
- response.getWriter().write(username+":Hello World!");
- }
- public boolean authenticate(){
- return true;
- }
- }
對於開發者來說,僅僅測試請求和返回信息是不夠的,所以HttpUnit提供的ServletRunner模擬器可以讓你對被調用Servlet內部的行爲進行測試。和簡單測試中不同,這裏使用了InvocationContext獲得該Servlet的環境,然後你可以通過InvocationContext對象針對request、response等對象或者是該Servlet的內部行爲(非服務方法)進行操作。
上述例子其實是junit的一個測試例子,在其中使用了httpunit模擬的servlet環境,使用上述方法測試servlet可以脫離容器,容易把該測試寫入ant或maven腳本,讓測試進行。httpunit網址:http://httpunit.sourceforge.net/
使用該種方法測試的弱點就是:如果要使用request(response)的setCharercterEncoding方法時,測試會出現一些問題,而且httpunit在測試servlet行爲時,採用的是完全模擬瀏覽器,有時測試比較難寫。
[注意]在測試Servlet的之前,你必須通過InvocationContext完成Servlet中的service方法中完成的工作,因爲通過newInvocation方法獲取InvocationContext實例的時候該方法並沒有被調用。
3. Httpunit測試小總結
- 模擬用戶行爲向服務器發送請求,傳遞參數
- 模擬用戶接受服務器的響應信息,並且通過輔助類分析這些響應信息,結合JUnit框架進行測試
- 使用HttpUnit提供的模擬Servler容器,測試開發中的Servlet的內部行爲
Mock objects方法的最大優點就是,執行測試時不需要運行的容器。可以很快就建立起測試,而且測試運行速度也很快用mock objects來對J2EE組件進行單元測試的缺點是:
- 它們沒有測試容器和組件的交互;
- 它們沒有測試組件的部署部分;
- 需要熟悉被調用的API,這樣才能模擬它,而這個要求可能太高了(尤其對於外部庫而言);
- 無法讓你確信代碼會在目標容器中正常運行