EasyMock的簡單使用(摘)
xp開發的一個利器--EasyMock。
EasyMock是一種 動態生成模仿對象以便應用于單元測試的工具,有了它可擺脫容器進行單元測試了。
它的使用很簡單,下面一個簡單登陸驗證的例子:
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
:
}
我們首先測試當用戶名和口令驗證失敗的情形,演示如何使用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();
}
然后,即可獲得 MockControl 創建的 Mock 對象:
下一步,我們需要“錄制” Mock 對象的預期行為。在 LoginServlet 中,先后調用了 request.getParameter("username") 和 request.getParameter("password") 兩個方法,因此,需要在 MockControl 中設置這兩次調用后的指定返回值。我們期望返回的值為“ admin ”和“ 1234 ”:
request.getParameter( " password " );? // ?期望下面的測試將調用此方法,參數為"?password"
mc.setReturnValue( " 1234 " ,? 1 );? // ?期望返回值為"1234",僅調用1次
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對象:
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();
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 ...
總結:
雖然
EasyMock
可以用來模仿依賴對象,但是,它只能動態模仿接口,無法模仿具體類。這一限制正好要求我們遵循“針對接口編程”的原則:如果不針對接口,則測試難于進行。應當把單元測試看作是運行時代碼的最好運用,如果代碼在單元測試中難于應用,則它在真實環境中也將難于應用。總之,創建盡可能容易測試的代碼就是創建高質量的代碼。
? 現在,easymock可以模仿類了,以下是在springside摘的:
????????bookManagerMockControl? = ?MockClassControl.createControl(BookManager. class );
????????bookManagerMock? = ?(BookManager)?bookManagerMockControl.getMock();
????????controller.setBookManager(bookManagerMock);
???????
// 錄制getAllBook()和getCategorys方法的期望值
????????bookManagerMock.getAllBook();
????????bookManagerMockControl.setReturnValue( new ?ArrayList());
????????bookManagerMockControl.replay();
????????
// 執行操作
????????mv? = ?controller.handleRequest(request,?response);
// 驗證結果?????????
????????assertModelAttributeAvailable(mv,? " books " );
posted on 2007-03-02 09:13 freebird 閱讀(554) 評論(0) 編輯 收藏 所屬分類: java