<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 1,  comments - 25,  trackbacks - 0

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

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


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

    類的單元測試仍然是極其困難的,例如,對數(shù)據(jù)庫操作的類進(jìn)行測試,如果不準(zhǔn)備好數(shù)據(jù)庫環(huán)境以

    及相關(guān)測試數(shù)據(jù),是很難進(jìn)行單元測試的;再例如,對需要運(yùn)行在容器內(nèi)的Servlet EJB 組件,脫

    離了容器也難于測試。

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

    測試中它們特別有價(jià)值。

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

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

    拋出指定的異常等。

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

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

    動態(tài)生成模仿對象以便應(yīng)用于單元測試。

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

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

    我們首先來看一個(gè)用戶驗(yàn)證的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. }  

     

    這個(gè)Servlet 實(shí)現(xiàn)簡單的用戶驗(yàn)證的功能,若用戶名和口令匹配“admin”和“123456”,則請求

    被轉(zhuǎn)發(fā)到指定的dispatcher 上,否則,直接拋出RuntimeException

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

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

    我們建立TestCase,名為LoginServletTest

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

     

    我們首先測試當(dāng)用戶名和口令驗(yàn)證失敗的情形, 演示如何使用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. }  

     

     

    仔細(xì)觀察測試代碼,使用EasyMock 來創(chuàng)建一個(gè)Mock 對象需要首先創(chuàng)建一個(gè)MockControl

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

     

     

    然后,即可獲得MockControl 創(chuàng)建的Mock 對象:

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

     

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

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

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

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

     

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

    我們對LoginServlet 進(jìn)行測試,并預(yù)期會產(chǎn)生一個(gè)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. }  

     

    由于本次測試的目的是檢查當(dāng)用戶名和口令驗(yàn)證失敗后, LoginServlet 是否會拋出

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

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

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

    下一步,我們來測試當(dāng)用戶名和口令匹配時(shí),LoginServlet 應(yīng)當(dāng)把請求轉(zhuǎn)發(fā)給指定的

    RequestDispatcher。在這個(gè)測試用例中,我們除了需要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()的語句順序,我們設(shè)定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()方法返回我們創(chuàng)建的ServletContext

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

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

     

    最后,檢查所有Mock 對象的狀態(tài):

     

     

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

    運(yùn)行JUnit,測試通過!

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

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

    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 閱讀(514) 評論(0)  編輯  收藏 所屬分類: Web Test Framework

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


    網(wǎng)站導(dǎo)航:
     
    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    常用鏈接

    留言簿(3)

    隨筆檔案

    文章分類

    文章檔案

    相冊

    搜索

    •  

    最新評論

    主站蜘蛛池模板: 免费国产99久久久香蕉| 国产一卡二卡3卡四卡免费| 啦啦啦www免费视频| 亚洲AV午夜福利精品一区二区| 国产亚洲蜜芽精品久久| 国产精品成人观看视频免费| 国产亚洲av人片在线观看| 亚洲成av人片天堂网无码】| 久久久久高潮毛片免费全部播放| 国产亚洲精品看片在线观看| 人人狠狠综合久久亚洲| 国产成人精品免费视频大全麻豆 | 久久久久久亚洲精品无码| 日韩av无码久久精品免费| 久久久久噜噜噜亚洲熟女综合| 亚洲色大成网站WWW国产| 51精品视频免费国产专区| 国产成人A人亚洲精品无码| 免费看一级毛片在线观看精品视频| 欧美日韩国产免费一区二区三区| 久久精品国产亚洲av麻豆小说 | 亚洲M码 欧洲S码SSS222| 亚洲午夜无码久久久久小说 | 91亚洲精品视频| 最近国语视频在线观看免费播放| 免费大黄网站在线观看| 亚洲乱妇老熟女爽到高潮的片| 成年人视频免费在线观看| 久久亚洲精品中文字幕| 国产色无码精品视频免费| 国产国拍亚洲精品福利 | 亚洲线精品一区二区三区影音先锋| 风间由美在线亚洲一区| 女人18毛片a级毛片免费视频| 亚洲人成电影网站| 蜜臀AV免费一区二区三区| 亚洲日本在线观看| 成人影片一区免费观看| 国产亚洲综合网曝门系列| 国产99视频精品免费视频76| 亚洲一区二区高清|