亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

使用EasyMock使單元測試更加容易

2019-11-18 16:13:04
字體:
來源:轉載
供稿:網友

幸運的是,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類:

/**

* LoginServlet.java

* Author: Liao Xue Feng, www.crackj2ee.com

*/

package com.crackj2ee.test.mock;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class LoginServlet extends HttpServlet {

PRotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

String username = request.getParameter("username");

String passWord = request.getParameter("password");

// check username & password:

if("admin".equals(username) && "123456".equals(password)) {

ServletContext context = getServletContext();

RequestDispatcher dispatcher = context.getNamedDispatcher("dispatcher");

dispatcher.forward(request, response);

}

else {

throw new RuntimeException("Login failed.");

}

}

}

這個Servlet實現簡單的用戶驗證的功能,若用戶名和口令匹配“admin”和“123456”,則請求被轉發到指定的dispatcher上,否則,直接拋出RuntimeException。

為了測試doPost()方法,我們需要模擬HttpServletRequest,ServletContext和RequestDispatcher對象,以便脫離J2EE容器來測試這個Servlet。

我們建立TestCase,名為LoginServletTest:

public class LoginServletTest extends TestCase {

}

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

public void testLoginFailed() throws Exception {

MockControl mc = MockControl.createControl(HttpServletRequest.class);

HttpServletRequest request = (HttpServletRequest)mc.getMock();

// set Mock Object behavior:

request.getParameter("username");

mc.setReturnValue("admin", 1);

request.getParameter("password");


mc.setReturnValue("1234", 1);

// ok, all behaviors are set!

mc.replay();

// now start test:

LoginServlet servlet = new LoginServlet();

try {

servlet.doPost(request, null);

fail("Not caught exception!");

}

catch(RuntimeException re) {

assertEquals("Login failed.", re.getMessage());

}

// verify:

mc.verify();

}

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

MockControl mc = MockControl.createControl(HttpServletRequest.class);

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

HttpServletRequest request = (HttpServletRequest)mc.getMock();

下一步,我們需要“錄制”Mock對象的預期行為。在LoginServlet中,先后調用了request.getParameter("username")和request.getParameter("password")兩個方法,因此,需要在MockControl中設置這兩次調用后的指定返回值。我們期望返回的值為“admin”和“1234”:

request.getParameter("username"); // 期望下面的測試將調用此方法,參數為"username"

mc.setReturnValue("admin", 1); // 期望返回值為"admin",僅調用1次

request.getParameter("password"); // 期望下面的測試將調用此方法,參數為" password"

mc.setReturnValue("1234", 1); // 期望返回值為"1234",僅調用1次

緊接著,調用mc.replay(),表示Mock對象“錄制”完畢,可以開始按照我們設定的方式運行,我們對LoginServlet進行測試,并預期會產生一個RuntimeException:

LoginServlet servlet = new LoginServlet();

try {

servlet.doPost(request, null);

fail("Not caught exception!");

}

catch(RuntimeException re) {

assertEquals("Login failed.", re.getMessage());

}

由于本次測試的目的是檢查當用戶名和口令驗證失敗后,LoginServlet是否會拋出RuntimeException,因此,response對象對測試沒有影響,我們不需要模擬它,僅僅傳入null即可。

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

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

下一步,我們來測試當用戶名和口令匹配時,LoginServlet應當把請求轉發給指定的RequestDispatcher。在這個測試用例中,我們除了需要HttpServletRequest Mock對象外,還需要模擬ServletContext和RequestDispatcher對象:

MockControl requestCtrl = MockControl.createControl(HttpServletRequest.class);

HttpServletRequest requestObj = (HttpServletRequest)requestCtrl.getMock();

MockControl contextCtrl = MockControl.createControl(ServletContext.class);

final ServletContext contextObj = (ServletContext)contextCtrl.getMock();

MockControl dispatcherCtrl = MockControl.createControl(RequestDispatcher.class);

RequestDispatcher dispatcherObj = (RequestDispatcher)dispatcherCtrl.getMock();

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


requestObj.getParameter("username");

requestCtrl.setReturnValue("admin", 1);

requestObj.getParameter("password");

requestCtrl.setReturnValue("123456", 1);

contextObj.getNamedDispatcher("dispatcher");

contextCtrl.setReturnValue(dispatcherObj, 1);

dispatcherObj.forward(requestObj, null);

dispatcherCtrl.setVoidCallable(1);

requestCtrl.replay();

contextCtrl.replay();

dispatcherCtrl.replay();

然后,測試doPost()方法,這里,為了讓getServletContext()方法返回我們創建的ServletContext Mock對象,我們定義一個匿名類并覆寫getServletContext()方法:

LoginServlet servlet = new LoginServlet() {

public ServletContext getServletContext() {

return contextObj;

}

};

servlet.doPost(requestObj, null);

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

requestCtrl.verify();

contextCtrl.verify();

dispatcherCtrl.verify();

運行JUnit,測試通過!

倘若LoginServlet的代碼有誤,例如,將context.getNamedDispatcher("dispatcher")誤寫為 context.getNamedDispatcher("dispatcher2"),則測試失敗,JUnit報告:

junit.framework.AssertionFailedError:

UneXPected method call getNamedDispatcher("dispatcher2"):

getNamedDispatcher("dispatcher2"): expected: 0, actual: 1

getNamedDispatcher("dispatcher"): expected: 1, actual: 0

at ...

完整的LoginServletTest代碼如下:

/**

* LoginServletTest.java

* Author: Liao Xue Feng, www.crackj2ee.com

*/

package com.crackj2ee.test.mock;

import javax.servlet.*;

import javax.servlet.http.*;

import org.easymock.*;

import junit.framework.TestCase;

public class LoginServletTest extends TestCase {

public void testLoginFailed() throws Exception {

MockControl mc = MockControl.createControl(HttpServletRequest.class);

HttpServletRequest request = (HttpServletRequest)mc.getMock();

// set Mock Object behavior:

request.getParameter("username");

mc.setReturnValue("admin", 1);

request.getParameter("password");

mc.setReturnValue("1234", 1);

// ok, all behaviors are set!

mc.replay();


// now start test:

LoginServlet servlet = new LoginServlet();

try {

servlet.doPost(request, null);

fail("Not caught exception!");

}

catch(RuntimeException re) {

assertEquals("Login failed.", re.getMessage());

}

// verify:

mc.verify();

}

public void testLoginOK() throws Exception {

// create mock:

MockControl requestCtrl = MockControl.createControl(HttpServletRequest.class);

HttpServletRequest requestObj = (HttpServletRequest)requestCtrl.getMock();

MockControl contextCtrl = MockControl.createControl(ServletContext.class);

final ServletContext contextObj = (ServletContext)contextCtrl.getMock();

MockControl dispatcherCtrl = MockControl.createControl(RequestDispatcher.class);

RequestDispatcher dispatcherObj = (RequestDispatcher)dispatcherCtrl.getMock();

// set behavior:

requestObj.getParameter("username");

requestCtrl.setReturnValue("admin", 1);

requestObj.getParameter("password");

requestCtrl.setReturnValue("123456", 1);

contextObj.getNamedDispatcher("dispatcher");

contextCtrl.setReturnValue(dispatcherObj, 1);

dispatcherObj.forward(requestObj, null);

dispatcherCtrl.setVoidCallable(1);

// done!

requestCtrl.replay();

contextCtrl.replay();

dispatcherCtrl.replay();

// test:

LoginServlet servlet = new LoginServlet() {

public ServletContext getServletContext() {

return contextObj;

}

};

servlet.doPost(requestObj, null);

// verify:

requestCtrl.verify();

contextCtrl.verify();

dispatcherCtrl.verify();

}

}

總結:

雖然EasyMock可以用來模仿依賴對象,但是,它只能動態模仿接口,無法模仿具體類。這一限制正好要求我們遵循“針對接口編程”的原則:如果不針對接口,則測試難于進行。應當把單元測試看作是運行時代碼的最好運用,如果代碼在單元測試中難于應用,則它在真實環境中也將難于應用??傊瑒摻ūM可能容易測試的代碼就是創建高質量的代碼。

(出處:http://www.49028c.com)



發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久久精品久久久| 国产成人精品一区二区| 九九九久久国产免费| 欧美在线视频免费观看| 欧美成人精品影院| 久久99热这里只有精品国产| 亚洲人成在线电影| 欧美激情中文字幕乱码免费| 亚洲美女性视频| 国产成人精品免高潮在线观看| 亚洲性无码av在线| 欧美猛交免费看| xxav国产精品美女主播| 国产亚洲视频在线观看| 欧美激情乱人伦| 亚洲欧美日韩一区二区三区在线| 亚洲网站在线看| 日韩精品黄色网| 欧美日韩精品在线观看| 国语自产精品视频在线看一大j8| 狠狠色狠色综合曰曰| 亚洲国产成人爱av在线播放| 91精品国产自产在线| 91在线播放国产| 高清欧美性猛交xxxx| 国产精品成人一区二区三区吃奶| 国产美女精品视频免费观看| 亚洲精品久久久久久久久久久久久| 在线不卡国产精品| 国产成人精品综合久久久| 久久成人免费视频| 91视频国产高清| 清纯唯美日韩制服另类| 日本免费在线精品| 中国日韩欧美久久久久久久久| 欧美日韩另类在线| 在线观看欧美日韩国产| 98精品在线视频| 亚洲国产日韩欧美综合久久| 欧美亚洲另类激情另类| 欧美日韩亚洲精品一区二区三区| 亚洲免费伊人电影在线观看av| 欧美一区在线直播| 欧美精品成人91久久久久久久| 久久精品中文字幕免费mv| 国a精品视频大全| 97精品国产97久久久久久| 久久久最新网址| 日韩视频在线观看免费| 8050国产精品久久久久久| 国产精品成人av性教育| 亚洲欧美日韩精品久久| 国产精品久久久久久久午夜| 国产亚洲一区二区精品| 日本成熟性欧美| 国产精品大陆在线观看| 亚洲国产精品成人精品| 午夜精品视频在线| 亚洲精品国产精品国自产观看浪潮| 中文字幕精品久久久久| 成人精品视频久久久久| 91影视免费在线观看| 91在线视频九色| 日韩成人在线观看| 91国产在线精品| 久久免费视频在线| 久久综合久久美利坚合众国| 国产精品情侣自拍| 欧美成人在线影院| 国产精品色午夜在线观看| 久久久久久这里只有精品| 日本成人精品在线| 91国产精品电影| 在线播放日韩精品| 欧美黑人巨大精品一区二区| 97久久久久久| 日本在线观看天堂男亚洲| 久久精品免费播放| 日韩高清电影免费观看完整| 亚洲成年人在线播放| 亚洲综合大片69999| 日韩一区二区av| 国产欧美在线看| 久久亚洲国产成人| 91tv亚洲精品香蕉国产一区7ujn| 久久天天躁狠狠躁夜夜av| 日韩成人中文字幕在线观看| 91精品国产91久久久久久吃药| 成人深夜直播免费观看| 日韩欧美视频一区二区三区| 在线视频中文亚洲| 精品久久久免费| 欧美亚洲第一页| 亚洲a成v人在线观看| 久久免费视频在线观看| 国产亚洲综合久久| 精品中文字幕视频| 亚洲偷熟乱区亚洲香蕉av| 性日韩欧美在线视频| 国产欧美精品久久久| 欧美一级视频在线观看| 国产精品1区2区在线观看| 国产精品久久久久久久久久久不卡| 国产精品十八以下禁看| x99av成人免费| 日韩成人av网址| 日韩美女av在线免费观看| 日韩一区二区av| 中文字幕自拍vr一区二区三区| 色哟哟网站入口亚洲精品| 亚洲理论片在线观看| 2018中文字幕一区二区三区| 国产成人av网址| www国产91| 亚洲乱码国产乱码精品精天堂| 亚洲人成啪啪网站| 日韩视频免费看| 精品视频在线播放| 国产一区二区免费| 国产精品久久久精品| 1769国产精品| 精品久久久香蕉免费精品视频| 国产欧美va欧美va香蕉在线| 日韩中文在线观看| 国产成人精品a视频一区www| 国产原创欧美精品| 国产精品视频一区二区高潮| 亚洲精品久久久久久下一站| 国产视频精品免费播放| 国产欧美va欧美va香蕉在| 久久高清视频免费| 98视频在线噜噜噜国产| 国产欧美va欧美va香蕉在| 欧美激情国产日韩精品一区18| 久久久久久久久久久成人| 韩国欧美亚洲国产| 欧美性猛交xxxx富婆| 日本精品久久中文字幕佐佐木| 日本一区二区三区在线播放| 国产成人精品日本亚洲专区61| 日韩欧美999| 亚洲一区美女视频在线观看免费| 亚洲xxxx3d| 亚洲精品99久久久久| 日本免费一区二区三区视频观看| 国产精品中文字幕在线观看| 国产精品久久久999| 青青草原成人在线视频| 国产欧美日韩亚洲精品| 午夜精品一区二区三区在线| 精品久久久在线观看| 国产精品永久免费观看| 91欧美激情另类亚洲| 国产综合香蕉五月婷在线| 欧美做爰性生交视频| 日韩在线视频观看正片免费网站| 国产日韩av高清| 国产99视频精品免视看7| 亚洲天堂免费视频| 日韩成人av网| 亚洲一区二区三区乱码aⅴ| 亚洲电影天堂av| 午夜剧场成人观在线视频免费观看| 国产女人18毛片水18精品|