日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

基于MOCK對(duì)象和JUNIT框架測(cè)試DAO

 Long_way 2007-04-23

對(duì)于Java組件開發(fā)者來說,他們都盼望擁有一組能夠?qū)M件開發(fā)提供全面測(cè)試功能的好用的單元測(cè)試。一直以來,與測(cè)試獨(dú)立的Java對(duì)象相比,測(cè)試傳統(tǒng)型J2EE Web組件是一項(xiàng)更為困難的任務(wù),因?yàn)閃eb組件必須運(yùn)行在某種服務(wù)器平臺(tái)上并且它們還要與基于HTTP的Web交互細(xì)節(jié)相聯(lián)系。

易測(cè)性(在框架中測(cè)試每個(gè)組件而不管其具體種類)是Spring框架所提倡的關(guān)鍵原則之一。從這一角度看,Spring是對(duì)核心J2EE模型的一個(gè)重大改進(jìn)—在以前情況下,在容器外進(jìn)行組件測(cè)試是很難實(shí)現(xiàn)的,而且即使是容器內(nèi)測(cè)試也往往要求復(fù)雜的安裝過程。

本文正是想集中探討Spring的易測(cè)性特征—它能使得對(duì)Web組件進(jìn)行單元測(cè)試就象測(cè)試普通Java對(duì)象(POJO)一樣容易。



一、Spring Mock類簡介

Mock對(duì)象是一個(gè)術(shù)語,原來主要流行于eXtreme程序員和JUnit小組中。在單元測(cè)試上下文中,一個(gè)mock對(duì)象是指這樣的一個(gè)對(duì)象——它能夠用一些“虛構(gòu)的占位符”功能來“模擬”實(shí)現(xiàn)一些對(duì)象接口。在測(cè)試過程中,這些虛構(gòu)的占位符對(duì)象可用簡單方式來模仿對(duì)于一個(gè)組件的期望的行為和結(jié)果,從而讓你專注于組件本身的徹底測(cè)試而不用擔(dān)心其它依賴性問題。

Spring從J2EE的Web端為每個(gè)關(guān)鍵接口提供了一個(gè)mock實(shí)現(xiàn):

MockHttpServletRequest—幾乎每個(gè)單元測(cè)試中都要使用這個(gè)類,它是J2EE Web應(yīng)用程序最常用的接口HttpServletRequest的mock實(shí)現(xiàn)。

MockHttpServletResponse—此對(duì)象用于HttpServletResponse接口的mock實(shí)現(xiàn)。

MockHttpSession—這是另外一個(gè)經(jīng)常使用的mock對(duì)象(后文將討論此類在會(huì)話綁定處理中的應(yīng)用)。

DelegatingServletInputStream—這個(gè)對(duì)象用于ServletInputStream接口的mock實(shí)現(xiàn)。

DelegatingServletOutputStream—這個(gè)對(duì)象將代理ServletOutputStream實(shí)現(xiàn)。在需要攔截和分析寫向一個(gè)輸出流的內(nèi)容時(shí),你可以使用它。

總之,在實(shí)現(xiàn)你自己的測(cè)試控制器時(shí),上面這些對(duì)象是最為有用的。然而,Spring也提供了下列相應(yīng)于其它不太常用的組件的mock實(shí)現(xiàn)(如果你是一個(gè)底層API開發(fā)者,那么你可能會(huì)找到其各自的相應(yīng)用法):

MockExpressionEvaluator—這個(gè)mock對(duì)象主要應(yīng)用于你想開發(fā)并測(cè)試你自己的基于JSTL的標(biāo)簽庫時(shí)。

MockFilterConfig—這是FilterConfig接口的一個(gè)mock實(shí)現(xiàn)。

MockPageContext—這是JSP PageContext接口的一個(gè)mock實(shí)現(xiàn)。你會(huì)發(fā)現(xiàn)這個(gè)對(duì)象的使用有利于測(cè)試預(yù)編譯的JSP。

MockRequestDispatcher—RequestDispatcher接口的一個(gè)mock實(shí)現(xiàn),你主要在其它mock對(duì)象內(nèi)使用它。

MockServletConfig—這是ServletConfig接口的一個(gè)mock實(shí)現(xiàn)。在單元測(cè)試某種Web組件(例如Struts框架所提供的Web組件)時(shí),要求你設(shè)置由MockServletContext所實(shí)現(xiàn)的ServletConfig和ServletContext接口。

那么,我們?cè)撊绾问褂眠@些mock對(duì)象呢?我們知道,HttpServletRequest是一個(gè)持有描述HTTP參數(shù)的固定值的組件,而正是這些參數(shù)驅(qū)動(dòng)Web組件的功能。MockHttpServletRequest,作為HttpServletRequest接口的一個(gè)實(shí)現(xiàn),允許你設(shè)置這些不可改變的參數(shù)。在典型的Web組件測(cè)試情形下,你可以實(shí)例化這個(gè)對(duì)象并按如下方式設(shè)置其中的任何參數(shù):
//指定表單方法和表單行為

MockHttpServletRequest request = new MockHttpServletRequest("GET", "/main.app");

request.addParameter("choice", expanded);request.addParameter("contextMenu", "left");

同樣地,你可以實(shí)例化并全面地控制和分析HttpResponse和HttpSession對(duì)象。接下來,讓我們簡要觀察Spring所提供的特定的JUnit框架擴(kuò)展。

二、JUnit框架擴(kuò)展

Spring提供了下列一些特定的JUnit框架擴(kuò)展:

AbstractDependencyInjectionSpringContextTests—這是一個(gè)針對(duì)所有測(cè)試的超類,其具體使用依賴于Spring上下文。

AbstractSpringContextTests—這是一個(gè)針對(duì)所有的JUnit測(cè)試情形的超類。它使用一個(gè)Spring上下文。并且,一般在測(cè)試中不是直接使用它,而是使用AbstractDependencyInjectionSpringContextTests或者AbstractTransactionalSpringContextTests這樣的派生類。

AbstractTransactionalSpringContextTests—這是一個(gè)針對(duì)所有測(cè)試的超類,我們一般把它應(yīng)用在事務(wù)相關(guān)的測(cè)試中。注意,一旦完成每個(gè)測(cè)試它就會(huì)正常地回滾事務(wù);而且你需要重載onSetUpInTransaction和onTearDownInTransaction方法以便手工開始并提交事務(wù)。

AbstractTransactionalDataSourceSpringContextTests—這是AbstractTransactionalSpringContextTests的一個(gè)子類,它使用了Spring的基于JDBC的jdbcTemplate工具類。
所有上面這些擴(kuò)展將極大程度地簡化在測(cè)試時(shí)對(duì)于相關(guān)操作的依賴性注入和事務(wù)管理。

三、普通Web測(cè)試情形

在此,我們將回顧測(cè)試Web組件的普通情形以及怎樣在其中使用Spring的mock對(duì)象和JUnit框架擴(kuò)展。

(一)確定一個(gè)正確的視圖

基于輸入?yún)?shù)生成正確的視圖可能是在操作一個(gè)Web應(yīng)用程序時(shí)最普通的功能。在Spring MVC的上下文中,這意味著Spring MVC將基于參數(shù)的狀態(tài)返回某種ModelAndView對(duì)象。你可以通過簡單地利用如下的Mock對(duì)象以一個(gè)常規(guī)JUnit測(cè)試方式來測(cè)試這項(xiàng)功能:

public void final testGettingToDetails throws Exception{

        MyController myController = new MyController(); myController.setDetailsView( detailsViewName );

        MockHttpServletRequest request = new MockHttpServletRequest();

        MockHttpServletResponse response = new MockHttpServletResponse();

        request.setMethod("POST");

         request.addParameter("viewDetails", "true");

        ModelAndView modelAndView = myController.handleRequest(request, response);

        assertEquals("Incorrect view name", detailsViewName,modelAndView.getViewName());

}

既然控制器很可能會(huì)利用一些服務(wù)對(duì)象來決定結(jié)果視圖,那么你還可以定制控制器中所用的這些mock服務(wù)對(duì)象。關(guān)于利用定制對(duì)象的更多資料,請(qǐng)參考mockobjects.com。


#p#(二)會(huì)話相關(guān)的操作

對(duì)于任何J2EE Web應(yīng)用程序來說,另一個(gè)必須實(shí)現(xiàn)的操作是HttpSession綁定處理。例如,Spring MVC可能需要決定是否一個(gè)對(duì)象處于會(huì)話中及其具體狀態(tài)以便產(chǎn)生正確的結(jié)果。你可以利用MockHttpSession對(duì)象和JUnit框架測(cè)試這種情形。請(qǐng)參考如下的代碼片斷:

public void testInvokesCorrectMethodWithSession() throws Exception {

        TestController cont = new TestController();

        MockHttpServletRequest request = new MockHttpServletRequest("GET", "/invoiceView.app");

        request.setSession(new MockHttpSession(null));

        HttpServletResponse response = new MockHttpServletResponse();

        ModelAndView mv = cont.handleRequest(request, response);

        assertTrue("Invoked loggedIn method", cont.wasInvoked("loggedIn"));

        assertTrue("view name is ",mv.getViewName().equals("loggedIn"));

        assertTrue("Only one method invoked", cont.getInvokedMethods() == 1);//測(cè)試控制器但是不使用會(huì)話

        request = new MockHttpServletRequest("GET", "/invoiceView.app");

        response = new MockHttpServletResponse();

        try  {

                cont.handleRequest(request, response);

                fail("Should have rejected request without session");

        } catch (ServletException ex) {//在此加入期盼的異常處理}}

(三)轉(zhuǎn)發(fā)和重定向

一個(gè)Spring MVC組件執(zhí)行的操作能夠?qū)е罗D(zhuǎn)發(fā)或重定向到另一個(gè)URL。如果你的目標(biāo)是分析轉(zhuǎn)發(fā)或重定向的結(jié)果,那么你可以測(cè)試這一情形—通過分析MockHttpResponse對(duì)象并進(jìn)而確定有哪些內(nèi)容包含在它的重定向或轉(zhuǎn)發(fā)值中,如下所示:

String responseString = ((MockHttpServletResponse)httpResponse).getForwardedUrl();

assertEquals( "Did not forward to the expected URL", responseString, expectedString);


四、生成正確的二進(jìn)制輸出

如何確定你有多少次必須實(shí)現(xiàn)“View as PDF”這一功能?下面的JUnit代碼片斷使用mock輸出流對(duì)象實(shí)現(xiàn)這一功能的正確測(cè)試:

public void testPDFGeneration() throws Exception {

        MockHttpServletRequest request = new MockHttpServletRequest();

        MockHttpServletResponse response = new MockHttpServletResponse();

        viewInvoiceAsPDFController.handleRequest( request, response );

         byte[] responsePDFValues = response.getContentAsByteArray();

        byte[] expectedPDFValues = loadBytesFromTestFile();

        assertTrue( "Did not generate expected PDF content.", Arrays.equals(responsePDFValues,expectedPDFValues ));

}


注意,在此你的控制器ViewInvoiceAsPDFController不是返回ModelAndView對(duì)象,而是產(chǎn)生了二進(jìn)制輸出—你可以使用一個(gè)二進(jìn)制的數(shù)組形式來捕獲此控制器并對(duì)此進(jìn)行正確性評(píng)價(jià)。

五、事務(wù)性單元測(cè)試

到目前為止,你已看到了相對(duì)簡單的JUnit測(cè)試—它僅發(fā)生在用mock對(duì)象支持的一個(gè)控制器的上下文中。但是,如果測(cè)試一個(gè)Web組件只有在一個(gè)事務(wù)性上下文(例如,通過依賴性注入與Hibernate集成到一起)中才有意義的情況又會(huì)怎么樣呢?不必?fù)?dān)心,Spring MVC為JUnit框架提供了一個(gè)體面的擴(kuò)展集合—它能準(zhǔn)確地提供依賴性注入和事務(wù)安全測(cè)試(也就是,任何更新在測(cè)試完成后都將被回滾)。

測(cè)試步驟:

讓我們看一種假想的情形—你要實(shí)現(xiàn)一個(gè)組件(例如MyTransactionalController)測(cè)試,該組件運(yùn)行在一個(gè)事務(wù)性的上下文中(也即,其方法調(diào)用的結(jié)果發(fā)生在一個(gè)事務(wù)內(nèi)并且它應(yīng)該在測(cè)試運(yùn)行完后被回滾):

1.創(chuàng)建一個(gè)定制的JUnit類(MyTransactionalControllerTest),它擴(kuò)展了Spring的JUnit擴(kuò)展類 AbstractTransactionalSpringContextTests:

import org.springframework.test.AbstractDependencyInjectionSpringContextTests;

public class MyTransactualControllerTest extends AbstractTransactionalSpringContextTests {

public class.


2.為了實(shí)現(xiàn)從Spring內(nèi)置的單元測(cè)試中發(fā)現(xiàn)Spring管理的bean,你需要重載getConfigLocations()方法并且返回上下文文件位置的String數(shù)組,請(qǐng)看如下:

protected abstract String[] getConfigLocations(){ return new String[] {"classpath:/test/spring-context.xml"};}


3.擁有該類的一個(gè)測(cè)試屬性及其相關(guān)聯(lián)的getter和setter。由于AbstractTransactionalSpringContextTests利用了auto-wiring(這是Spring框架的一個(gè)特性—能夠根據(jù)類屬性的名字識(shí)別類依賴性并且用Spring bean填入相匹配的名字或ID)技術(shù)而且在測(cè)試時(shí)它將自動(dòng)地解決類的依賴性問題,所以在Spring上下文文件中該類屬性具有與Spring管理的bean一樣的名字并且在測(cè)試時(shí)每個(gè)屬性都有一個(gè)適當(dāng)命名的setter:

public MyTransactualController myTransactualController;

/** * @返回myTransactualController。 */

public MyTransactualController getMyTransactualController()

{

 return this.myTransactualController;

}

/** *@參數(shù)myTransactualController。 */public void setMyTransactualController( MyTransactualController myTransactualController) { this.myTransactualController = myTransactualController;

}


4.就象你通常操作“普通的”JUnit測(cè)試一樣實(shí)現(xiàn)測(cè)試方法:

public void testCorrectBehavior() throws Exception{ //運(yùn)行該事務(wù)性方法 myTransactualController.submitPayment( new Payment( 100 ) );

assertTrue( myTransactualController.isValid() );}

注意,你是在調(diào)用可能會(huì)更新數(shù)據(jù)庫的方法submitPayment。Spring的JUnit擴(kuò)展(AbstractTransactionalSpringContextTests)將在這個(gè)測(cè)試方法結(jié)束后實(shí)現(xiàn)自動(dòng)回滾。

5.如果你需要執(zhí)行任何安裝或清除任務(wù),則可以重載AbstractTransactionalSpringContextTests的onSetUpBeforeTransaction()或onSetUpInTransaction()方法。AbstractTransactionalSpringContextTests將重載從TestCase繼承來的setUp()和tearDown()方法并且使其成為final類型。

六、小結(jié)

至此,你已經(jīng)學(xué)習(xí)了如何使用Spring單元測(cè)試框架和Web組件mock對(duì)象。通過使用這兩個(gè)工具,你將會(huì)極大地提高你的Web組件的開發(fā)效率。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多