posts - 1,  comments - 25,  trackbacks - 0

          最近開始玩WEB相關的技術,在這里先極度的鄙視一下那些這要功能而不注重TestCase的菜鳥們,

          好了,不把工作上的東東帶到這里來。


          單元測試是Agile 極力推薦的測試驅動開發模式,是保證軟件質量的重要方法。盡管如此,對許多

          類的單元測試仍然是極其困難的,例如,對數據庫操作的類進行測試,如果不準備好數據庫環境以

          及相關測試數據,是很難進行單元測試的;再例如,對需要運行在容器內的Servlet EJB 組件,脫

          離了容器也難于測試。

          幸運的是,Mock Object 可以用來模擬一些我們需要的類,這些對象被稱之為模仿對象,在單元

          測試中它們特別有價值。

          Mock Object 用于模仿真實對象的方法調用,從而使得測試不需要真正的依賴對象。Mock Object

          只為某個特定的測試用例的場景提供剛好滿足需要的最少功能。它們還可以模擬錯誤的條件,例如

          拋出指定的異常等。

          目前,有許多可用的Mock 類庫可供我們選擇。一些Mock 庫提供了常見的模仿對象,例如:

          HttpServletRequest,而另一些Mock 庫則提供了動態生成模仿對象的功能,本文將討論使用EasyMock

          動態生成模仿對象以便應用于單元測試。

          到目前為止,EasyMock 提供了1.2 版本和2.0 版本,2.0 版本僅支持Java SE 5.0,本例中,我們

          選擇EasyMock 1.2 for Java 1.3 版本進行測試,可以從http://www.easymock.org 下載合適的版本。

          我們首先來看一個用戶驗證的LoginServlet 類:

          Java代碼  收藏代碼
          1. /** 
          2. * LoginServlet.java 
          3. */  
          4. package com.javaeedev.test.mock;  
          5. import java.io.*;  
          6. import javax.servlet.*;  
          7. import javax.servlet.http.*;  
          8.   
          9. public class LoginServlet extends HttpServlet {  
          10.     protected void doPost(HttpServletRequest request, HttpServletResponse response)  
          11.         throws ServletException, IOException {  
          12.             String username = request.getParameter("username");  
          13.             String password = request.getParameter("password");  
          14.             // check username & password:  
          15.             if("admin".equals(username) && "123456".equals(password)) {  
          16.                 ServletContext context = getServletContext();  
          17.                 RequestDispatcher dispatcher = context.getNamedDispatcher("dispatcher");  
          18.                 dispatcher.forward(request, response);  
          19.             }else {  
          20.                 throw new RuntimeException("Login failed.");  
          21.             }  
          22.     }  
          23. }  

           

          這個Servlet 實現簡單的用戶驗證的功能,若用戶名和口令匹配“admin”和“123456”,則請求

          被轉發到指定的dispatcher 上,否則,直接拋出RuntimeException

          為了測試doPost()方法,我們需要模擬HttpServletRequestServletContext RequestDispatcher

          對象,以便脫離J2EE 容器來測試這個Servlet

          我們建立TestCase,名為LoginServletTest

          Java代碼  收藏代碼
          1. public class LoginServletTest extends TestCase {  
          2. }  

           

          我們首先測試當用戶名和口令驗證失敗的情形, 演示如何使用EasyMock 來模擬

           

          HttpServletRequest 對象:

          Java代碼  收藏代碼
          1. public void testLoginFailed() throws Exception {  
          2.     MockControl mc = MockControl.createControl(HttpServletRequest.class);  
          3.     HttpServletRequest request = (HttpServletRequest)mc.getMock();  
          4.     // set Mock Object behavior:  
          5.     request.getParameter("username");  
          6.     mc.setReturnValue("admin"1);  
          7.     request.getParameter("password");  
          8.     mc.setReturnValue("1234"1);  
          9.     // ok, all behaviors are set!  
          10.     mc.replay();  
          11.     // now start test:  
          12.     LoginServlet servlet = new LoginServlet();  
          13.     try {  
          14.         servlet.doPost(request, null);  
          15.         fail("Not caught exception!");  
          16.     }catch(RuntimeException re) {  
          17.         assertEquals("Login failed.", re.getMessage());  
          18.     }  
          19.     // verify:  
          20.     mc.verify();  
          21. }  

           

           

          仔細觀察測試代碼,使用EasyMock 來創建一個Mock 對象需要首先創建一個MockControl

          Java代碼  收藏代碼
          1. MockControl mc = MockControl.createControl(HttpServletRequest.class);  

           

           

          然后,即可獲得MockControl 創建的Mock 對象:

          Java代碼  收藏代碼
          1. HttpServletRequest request = (HttpServletRequest)mc.getMock();  

           

          下一步,我們需要“錄制”Mock 對象的預期行為。LoginServlet 中, 先后調用了

          request.getParameter("username") request.getParameter("password") 兩個方法, 因此, 需要在

          MockControl 中設置這兩次調用后的指定返回值。我們期望返回的值為“admin”和“1234”:

          Java代碼  收藏代碼
          1. request.getParameter("username"); // 期望下面的測試將調用此方法,參數為"username"  
          2. mc.setReturnValue("admin"1); // 期望返回值為"admin",僅調用1 次  
          3. request.getParameter("password"); // 期望下面的測試將調用此方法,參數為" password"  
          4. mc.setReturnValue("1234"1); // 期望返回值為"1234",僅調用1 次  

           

          緊接著,調用mc.replay(),表示Mock 對象“錄制”完畢,可以開始按照我們設定的方式運行,

          我們對LoginServlet 進行測試,并預期會產生一個RuntimeException

           

           

          Java代碼  收藏代碼
          1. LoginServlet servlet = new LoginServlet();  
          2. try {  
          3.     servlet.doPost(request, null);  
          4.     fail("Not caught exception!");  
          5. }catch(RuntimeException re) {  
          6.     assertEquals("Login failed.", re.getMessage());  
          7. }  

           

          由于本次測試的目的是檢查當用戶名和口令驗證失敗后, LoginServlet 是否會拋出

          RuntimeException,因此,response 對象對測試沒有影響,我們不需要模擬它,僅僅傳入null 即可。

          最后,調用mc.verify()檢查Mock 對象是否按照預期的方法調用正常運行了。

          運行JUnit,測試通過!表示我們的Mock 對象正確工作了!

          下一步,我們來測試當用戶名和口令匹配時,LoginServlet 應當把請求轉發給指定的

          RequestDispatcher。在這個測試用例中,我們除了需要HttpServletRequest Mock 對象外,還需要模擬

          ServletContext RequestDispatcher 對象:

           

           

          Java代碼  收藏代碼
          1. MockControl requestCtrl = MockControl.createControl(HttpServletRequest.class);  
          2. HttpServletRequest requestObj = (HttpServletRequest)requestCtrl.getMock();  
          3. MockControl contextCtrl = MockControl.createControl(ServletContext.class);  
          4. final ServletContext contextObj = (ServletContext)contextCtrl.getMock();  
          5. MockControl dispatcherCtrl =MockControl.createControl(RequestDispatcher.class);  
          6. RequestDispatcher dispatcherObj = (RequestDispatcher)dispatcherCtrl.getMock();  

           

          按照doPost()的語句順序,我們設定Mock 對象指定的行為:

          Java代碼  收藏代碼
          1. requestObj.getParameter("username");  
          2. requestCtrl.setReturnValue("admin"1);  
          3. requestObj.getParameter("password");  
          4. requestCtrl.setReturnValue("123456"1);  
          5. contextObj.getNamedDispatcher("dispatcher");  
          6. contextCtrl.setReturnValue(dispatcherObj, 1);  
          7. dispatcherObj.forward(requestObj, null);  
          8. dispatcherCtrl.setVoidCallable(1);  
          9. requestCtrl.replay();  
          10. contextCtrl.replay();  
          11. dispatcherCtrl.replay();  

           

          然后,測試doPost()方法,這里,為了讓getServletContext()方法返回我們創建的ServletContext

          Mock 對象,我們定義一個匿名類并覆寫getServletContext()方法:

          Java代碼  收藏代碼
          1. LoginServlet servlet = new LoginServlet() {  
          2.     public ServletContext getServletContext() {  
          3.         return contextObj;  
          4.     }  
          5. };  
          6. servlet.doPost(requestObj, null);  

           

          最后,檢查所有Mock 對象的狀態:

           

           

          Java代碼  收藏代碼
          1. requestCtrl.verify();  
          2. contextCtrl.verify();  
          3. dispatcherCtrl.verify();  

          運行JUnit,測試通過!

          倘若LoginServlet 的代碼有誤, 例如, 將context.getNamedDispatcher("dispatcher") 誤寫為

          context.getNamedDispatcher("dispatcher2"),則測試失敗,JUnit 報告:

          Java代碼  收藏代碼
          1. junit.framework.AssertionFailedError:  
          2. Unexpected method call getNamedDispatcher("dispatcher2"):  
          3. getNamedDispatcher("dispatcher2"): expected: 0, actual: 1  
          4. getNamedDispatcher("dispatcher"): expected: 1, actual: 0  
          5. at ...  

           

          完整的LoginServletTest 代碼如下:

          Java代碼  收藏代碼
          1. /** 
          2.  * LoginServletTest.java 
          3.  */  
          4. package com.javaeedev.test.mock;  
          5.   
          6. import javax.servlet.*;  
          7. import javax.servlet.http.*;  
          8. import org.easymock.*;  
          9. import junit.framework.TestCase;  
          10.   
          11. public class LoginServletTest extends TestCase {  
          12.     public void testLoginFailed() throws Exception {  
          13.         MockControl mc = MockControl.createControl(HttpServletRequest.class);  
          14.         HttpServletRequest request = (HttpServletRequest)mc.getMock();  
          15.         // set Mock Object behavior:  
          16.         request.getParameter("username");  
          17.         mc.setReturnValue("admin"1);  
          18.         request.getParameter("password");  
          19.         mc.setReturnValue("1234"1);  
          20.         // ok, all behaviors are set!  
          21.         mc.replay();  
          22.         // now start test:  
          23.         LoginServlet servlet = new LoginServlet();  
          24.         try {  
          25.             servlet.doPost(request, null);  
          26.             fail("Not caught exception!");  
          27.         }catch(RuntimeException re) {  
          28.             assertEquals("Login failed.", re.getMessage());  
          29.         }  
          30.         // verify:  
          31.         mc.verify();  
          32.     }  
          33.     public void testLoginOK() throws Exception {  
          34.         // create mock:  
          35.         MockControl requestCtrl = MockControl.createControl(HttpServletRequest.class);  
          36.         HttpServletRequest requestObj = (HttpServletRequest)requestCtrl.getMock();  
          37.         MockControl contextCtrl = MockControl.createControl(ServletContext.class);  
          38.         final ServletContext contextObj = (ServletContext)contextCtrl.getMock();  
          39.         MockControl dispatcherCtrl =  
          40.         MockControl.createControl(RequestDispatcher.class);  
          41.         RequestDispatcher dispatcherObj = (RequestDispatcher)dispatcherCtrl.getMock();  
          42.         // set behavior:  
          43.         requestObj.getParameter("username");  
          44.         requestCtrl.setReturnValue("admin"1);  
          45.         requestObj.getParameter("password");  
          46.         requestCtrl.setReturnValue("123456"1);  
          47.         contextObj.getNamedDispatcher("dispatcher");  
          48.         contextCtrl.setReturnValue(dispatcherObj, 1);  
          49.         dispatcherObj.forward(requestObj, null);  
          50.         dispatcherCtrl.setVoidCallable(1);  
          51.         // done!  
          52.         requestCtrl.replay();  
          53.         contextCtrl.replay();  
          54.         dispatcherCtrl.replay();  
          55.         // test:  
          56.         LoginServlet servlet = new LoginServlet() {  
          57.             public ServletContext getServletContext() {  
          58.                 return contextObj;  
          59.             }  
          60.         };  
          61.         servlet.doPost(requestObj, null);  
          62.         // verify:  
          63.         requestCtrl.verify();  
          64.         contextCtrl.verify();  
          65.         dispatcherCtrl.verify();  
          66.     }  
          67. }  
          posted on 2011-04-28 23:39 Daniel 閱讀(517) 評論(0)  編輯  收藏 所屬分類: Web Test Framework

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          常用鏈接

          留言簿(3)

          隨筆檔案

          文章分類

          文章檔案

          相冊

          搜索

          •  

          最新評論

          主站蜘蛛池模板: 闻喜县| 西昌市| 青神县| 海门市| 大埔县| 横山县| 钟山县| 古田县| 和平区| 墨脱县| 克山县| 安阳市| 平泉县| 襄樊市| 华宁县| 探索| 柳江县| 体育| 盘山县| 咸丰县| 历史| 新营市| 临城县| 右玉县| 双鸭山市| 会东县| 海安县| 桦甸市| 丽水市| 汤原县| 平泉县| 宁蒗| 固原市| 湄潭县| 庐江县| 门源| 公安县| 通渭县| 宁阳县| 桓仁| 武冈市|