<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 【第十三章】 測試 之 13.1 概述 13.2 單元測試 ——跟我學spring3 ## 13.1? 概述 ### 13.1.1? 測試 軟件測試的目的首先是為了保證軟件功能的正確性,其次是為了保證軟件的質量,軟件測試相當復雜,已經超出本書所涉及的范圍,本節將只介紹軟件測試流程中前兩個步驟:單元測試和集成測試。 Spring提供了專門的測試模塊用于簡化單元測試和集成測試,單元測試和集成測試一般由程序員實現。 ## 13.2? 單元測試 ### 13.2.1? 概述 單元測試是最細粒度的測試,即具有原子性,通常測試的是某個功能(如測試類中的某個方法的功能)。 采用依賴注入后我們的代碼對Spring IoC容器幾乎沒有任何依賴,因此在對我們代碼進行測試時無需依賴Spring IoC容器,我們只需要通過簡單的實例化對象、注入依賴然后測試相應方法來測試該方法是否完成我們預期的功能。 在本書中使用的傳統開發流程,即先編寫代碼實現功能,然后再寫測試來驗證功能是否正確,而不是測試驅動開發,測試驅動開發是指在編寫代碼實現功能之前先寫測試,然后再根據測試來寫滿足測試要求的功能代碼,通過測試來驅動開發,如果對測試驅動開發感興趣推薦閱讀【測試驅動開發的藝術】。 在實際工作中,應該只對一些復雜的功能進行單元測試,對于一些簡單的功能(如數據訪問層的CRUD)沒有必要花費時間進行單元測試。 Spring對單元測試提供如下支持: * Mock對象:Spring通過Mock對象來簡化一些場景的單元測試: **JNDI測試支持**:在org.springframework.mock.jndi包下通過了SimpleNamingContextBuilder來來創建JNDI上下文Mock對象,從而無需依賴特定Java EE容器即可完成JNDI測試。 **web測試支持:**在org.springframework.mock.web包中提供了一組Servlet API的Mock對象,從而可以無需Web容器即可測試web層的類。 * **工具類**:通過通用的工具類來簡化編寫測試代碼: **反射工具類:**在org.springframework.test.util包下的ReflectionTestUtils能通過反射完成類的非public字段或setter方法的調用; **JDBC工具類:**在org.springframework.test.util包下的SimpleJdbcTestUtils能讀取一個sql腳本文件并執行來簡化SQL的執行,還提供了如清空表、統計表中行數的簡便方法來簡化測試代碼的編寫。 接下來讓我們學習一下開發過程中各層代碼如何編寫測試用例。 ### 13.2.2? 準備測試環境 **1、Junit安裝:**將Junit 4包添加到“pointShop”項目中,具體方法請參照【2.2.3? Hello World】。 **?????? 2、jMock安裝:**到jMock官網【http://www.jmock.org/】下載最新的jMock包,在本書中使用jMock2.5.1版本,將下載的“jmock-2.5.1-jars.zip ”包中的如下jar包拷貝到項目的lib目錄下并添加到類路徑: + objenesis-1.0.jar + jmock-script-2.5.1.jar + jmock-legacy-2.5.1.jar + jmock-junit4-2.5.1.jar + jmock-junit3-2.5.1.jar + jmock-2.5.1.jar + hamcrest-library-1.1.jar + hamcrest-core-1.1.jar + bsh-core-2.0b4.jar 注:cglib包無需添加到類路徑,因為我們之前已經提供。 **3、添加Spring測試支持包:**將下載的spring-framework-3.0.5.RELEASE-with-docs.zip包中的如下jar包拷貝到項目的lib目錄下并添加到類路徑: dist\org.springframework.test-3.0.5.RELEASE.jar 4、在“pointShop”項目下新建test文件夾并將其添加到【Java Build Path】中,該文件夾用于存放測試代碼,從而分離測試代碼和開發代碼。 到此測試環境搭建完畢。 ### 13.2.3? 數據訪問層 數據訪問層單元測試,目的是測試該層定義的接口實現方法的行為是否正確,其實本質是測試是否正確與數據庫交互,是否發送并執行了正確的SQL,SQL執行成功后是否正確的組裝了業務邏輯層需要的數據。 數據訪問層單元測試通過Mock對象與數據庫交互的API來完成測試。 接下來讓我們學習一下如何進行數據訪問層單元測試: **1、在test文件夾下創建如下測試類:** ``` package cn.javass.point.dao.hibernate; //省略import public class GoodsHibernateDaoUnitTest { //1、Mock對象上下文,用于創建Mock對象 private final Mockery context = new Mockery() {{ //1.1、表示可以支持Mock非接口類,默認只支持Mock接口 setImposteriser(ClassImposteriser.INSTANCE); }}; //2、Mock HibernateTemplate類 private final HibernateTemplate mockHibernateTemplate = context.mock(HibernateTemplate.class); private IGoodsDao goodsDao = null; @Before public void setUp() { //3、創建IGoodsDao實現 GoodsHibernateDao goodsDaoTemp = new GoodsHibernateDao(); //4、通過ReflectionTestUtils注入需要的非public字段數據 ReflectionTestUtils.setField(goodsDaoTemp, "entityClass", GoodsModel.class); //5、注入mockHibernateTemplate對象 goodsDaoTemp.setHibernateTemplate(mockHibernateTemplate); //6、賦值給我們要使用的接口 goodsDao = goodsDaoTemp; } } ``` * Mockery:jMock核心類,用于創建Mock對象的,通過其mock方法來創建相應接口或類的Mock對象。 * goodsDaoTemp:需要測試的IGoodsDao實現,通過ReflectionTestUtils注入需要的非public字段數據。 **2、測試支持寫完后,接下來測試一下IGoodsDao的get方法是否滿足需求:** ``` @Test public void testSave () { //7、創建需要的Model數據 final GoodsModel expected = new GoodsModel(); //8、定義預期行為,并在后邊來驗證預期行為是否正確 context.checking(new org.jmock.Expectations() { { //9、表示需要調用且只調用一次mockHibernateTemplate的get方法, //且get方法參數為(GoodsModel.class, 1),并將返回goods one(mockHibernateTemplate).get(GoodsModel.class, 1); will(returnValue(expected)); } }); //10、調用goodsDao的get方法,在內部實現中將委托給 //getHibernateTemplate().get(this.entityClass, id); //因此按照第8步定義的預期行為將返回goods GoodsModel actual = goodsDao.get(1); //11、來驗證第8步定義的預期行為是否調用了 context.assertIsSatisfied(); //12、驗證goodsDao.get(1)返回結果是否正確 Assert.assertEquals(goods, expected); } ``` * **context.checking():**該方法中用于定義預期行為,其中第9步定義了需要調用一次且只調用一次mockHibernateTemplate的get方法,且get方法參數為(GoodsModel.class, 1),并將返回goods對象。 * **goodsDao.get(1):**調用goodsDao的get方法,在內部實現中將委托給“getHibernateTemplate().get(this.entityClass, id)”。 * **context.assertIsSatisfied():**來驗證前邊定義的預期行為是否執行,且是否正確。 * **Assert.assertEquals(expected, actual):**用于驗證“goodsDao.get(1) ”返回的結果是否是預期結果。 以上測試方法其實是沒有必要的,對于非常簡單的CRUD沒有必要寫單元測試,只有相當復雜的方法才有必要寫單元測試。 這種通過Mock對象來測試數據訪問層代碼其實一點意義沒有,因為這里沒有與數據庫交互,無法驗證真實環境中與數據庫交互是否正確,因此這里只是告訴你如何測試數據訪問層代碼,在實際工作中一般通過集成測試來完成數據訪問層測試。 ### 13.2.4? 業務邏輯層 業務邏輯單元測試,目的是測試該層的業務邏輯是否正確并通過Mock 數據訪問層對象來隔離與數據庫交互,從而無需連接數據庫即可測試業務邏輯是否正確。 接下來讓我們學習一下如何進行業務邏輯層單元測試: **1、在test文件夾下創建如下測試類:** ``` package cn.javass.point.service.impl; //省略import public class GoodsCodeServiceImplUnitTest { //1、Mock對象上下文,用于創建Mock對象 private final Mockery context = new Mockery() {{ //1.1、表示可以支持Mock非接口類,默認只支持Mock接口 setImposteriser(ClassImposteriser.INSTANCE); }}; //2、Mock IGoodsCodeDao接口 private IGoodsCodeDao goodsCodeDao = context.mock(IGoodsCodeDao.class);; private IGoodsCodeService goodsCodeService; @Before public void setUp() { GoodsCodeServiceImpl goodsCodeServiceTemp = new GoodsCodeServiceImpl(); //3、依賴注入 goodsCodeServiceTemp.setDao(goodsCodeDao); goodsCodeService = goodsCodeServiceTemp; } } ``` 以上測試支持代碼和數據訪問層測試代碼非常類似,在此不再闡述。 **2、測試支持寫完后,接下來測試一下購買商品Code碼是否滿足需求:** 測試業務邏輯時需要分別測試多種場景,即如在某種場景下成功或失敗等等,即測試應該全面,每個功能點都應該測試到。 **2.1、測試購買失敗的場景:** ``` @Test(expected = NotCodeException.class) public void testBuyFail() { final int goodsId = 1; //4、定義預期行為,并在后邊來驗證預期行為是否正確 context.checking(new org.jmock.Expectations() { { //5、表示需要調用goodsCodeDao對象的getOneNotExchanged一次且僅以此 //且返回值為null one(goodsCodeDao).getOneNotExchanged(goodsId); will(returnValue(null)); } }); goodsCodeService.buy("test", goodsId); context.assertIsSatisfied(); } ``` * **context.checking():**該方法中用于定義預期行為,其中第5步定義了需要調用一次且只調用一次goodsCodeDao的getOneNotExchanged方法,且getOneNotExchanged方法參數為(goodsId),并將返回null。 * **goodsCodeService.buy("test", goodsId):**調用goodsCodeService的buy方法,由于調用goodsCodeDao的getOneNotExchanged方法將返回null,因此buy方法將拋出“NotCodeException”異常,從而表示沒有Code碼。 * **context.assertIsSatisfied():**來驗證前邊定義的預期行為是否執行,且是否正確。 * 由于我們在預期行為中調用getOneNotExchanged將返回null,因此測試將失敗且拋出NotCodeException異常。 **2.2、測試購買成功的場景:** ``` @Test() public void testBuySuccess () { final int goodsId = 1; final GoodsCodeModel goodsCode = new GoodsCodeModel(); //6、定義預期行為,并在后邊來驗證預期行為是否正確 context.checking(new org.jmock.Expectations() { { //7、表示需要調用goodsCodeDao對象的getOneNotExchanged一次且僅以此 //且返回值為null one(goodsCodeDao).getOneNotExchanged(goodsId); will(returnValue(goodsCode)); //8、表示需要調用goodsCodeDao對象的save方法一次且僅一次 //且參數為goodsCode one(goodsCodeDao).save(goodsCode); } }); goodsCodeService.buy("test", goodsId); context.assertIsSatisfied(); Assert.assertEquals(goodsCode.isExchanged(), true); } ``` * **context.checking():**該方法中用于定義預期行為,其中第7步定義了需要調用一次且只調用一次goodsCodeDao的getOneNotExchanged方法,且getOneNotExchanged方法參數為(goodsId),并將返回goodsCode對象;第8步定義了需要調用goodsCodeDao對象的save一次且僅一次。 * **goodsCodeService.buy("test", goodsId):**調用goodsCodeService的buy方法,由于調用goodsCodeDao的getOneNotExchanged方法將返回goodsCode,因此buy方法將成功執行。 * **context.assertIsSatisfied():**來驗證前邊定義的預期行為是否執行,且是否正確。 * **Assert.assertEquals(goodsCode.isExchanged(), true):**表示goodsCode已經被購買過了。 * 由于我們在預期行為中調用getOneNotExchanged將返回一個goodsCode對象,因此測試將成功,如果失敗說明業務邏輯寫錯了。 到此業務邏輯層測試完畢,在進行業務邏輯層測試時我們只關心業務邏輯是否正確,而不關心底層數據訪問層如何實現,因此測試業務邏輯層時只需Mock 數據訪問層對象,然后定義一些預期行為來滿足業務邏輯測試需求即可。 ### 13.2.5? 表現層 表現層測試包括如Struts2的Action單元測試、攔截器單元測試、JSP單元測試等等,在此我們只學習Struts2的Action單元測試。 Struts2的Action測試相對業務邏輯層測試相對復雜一些,因為牽扯到使用如Servlet API、ActionContext等等,這里需要通過stub(樁)實現或mock對象來模擬如HttpServletRequest等對象。 一、首先學習一些最簡單的Action測試: **1、在test文件夾下創建如下測試類:** ``` package cn.javass.point.web.front; import cn.javass.point.service.IGoodsCodeService; import cn.javass.point.web.front.action.GoodsAction; //省略部分import public class GoodsActionUnitTest { //1、Mock對象上下文,用于創建Mock對象 private final Mockery context = new Mockery() {{ //1.1、表示可以支持Mock非接口類,默認只支持Mock接口 setImposteriser(ClassImposteriser.INSTANCE); }}; //2、Mock IGoodsCodeService接口 private IGoodsCodeService goodsCodeService = context.mock(IGoodsCodeService.class); private GoodsAction goodsAction; @Before public void setUp() { goodsAction = new GoodsAction(); //3、依賴注入 goodsAction.setGoodsCodeService(goodsCodeService); } } ``` 以上測試支持代碼和業務邏輯層測試代碼非常類似,在此不再闡述。 **2、測試支持寫完后,接下來測試一下前臺購買商品Code碼是否滿足需求:** 類似于測試業務邏輯時需要分別測試多種場景,測試Action同樣需要分別測試多種場景。 **2.1、測試購買失敗的場景:** ``` @Test public void testBuyFail() { final int goodsId = 1; //4、定義預期行為,并在后邊來驗證預期行為是否正確 context.checking(new org.jmock.Expectations() { { //5、表示需要調用goodsCodeService對象的buy方法一次且僅一次 //且拋出NotCodeException異常 one(goodsCodeService).buy("test", goodsId); will(throwException(new NotCodeException())); } }); //6、模擬Struts注入請求參數 goodsAction.setGoodsId(goodsId); String actualResultCode = goodsAction.buy(); context.assertIsSatisfied(); Assert.assertEquals(ReflectionTestUtils.getField(goodsAction, "BUY_RESULT"), actualResultCode); Assert.assertTrue(goodsAction.getActionErrors().size() > 0); } ``` * **context.checking():**該方法中用于定義預期行為,其中第5步定義了需要調用goodsCodeService對象的buy方法一次且僅一次且將拋出NotCodeException異常。 * **goodsAction.setGoodsId(goodsId)**:用于模擬Struts注入請求參數,即完成數據綁定。 * **goodsAction.buy():**調用goodsAction的buy方法,該方法將委托給IGoodsCodeService實現完成,返回值用于定位視圖。 * **context.assertIsSatisfied():**來驗證前邊定義的預期行為是否執行,且是否正確。 * **Assert.assertEquals(ReflectionTestUtils.getField(goodsAction, "BUY_RESULT"), actualResultCode)**:驗證返回的Result是否是我們指定的。 * **Assert.assertTrue(goodsAction.getActionErrors().size() &gt; 0)**:表示執行Action時有錯誤,即Action動作錯誤。如果條件不成立,說明我們Action功能是錯誤的,需要修改。 **2.2、測試購買成功的場景:** ``` @Test public void testBuySuccess() { final int goodsId = 1; final GoodsCodeModel goodsCode = new GoodsCodeModel(); //7、定義預期行為,并在后邊來驗證預期行為是否正確 context.checking(new org.jmock.Expectations() { { //8、表示需要調用goodsCodeService對象的buy方法一次且僅一次 //且返回goodsCode對象 one(goodsCodeService).buy("test", goodsId); will(returnValue(goodsCode)); } }); //9、模擬Struts注入請求參數 goodsAction.setGoodsId(goodsId); String actualResultCode = goodsAction.buy(); context.assertIsSatisfied(); Assert.assertEquals(ReflectionTestUtils.getField(goodsAction, "BUY_RESULT"), actualResultCode); Assert.assertTrue(goodsAction.getActionErrors().size() == 0); } ``` * **context.checking():**該方法中用于定義預期行為,其中第5步定義了需要調用goodsCodeService對象的buy方法一次且僅一次且將返回goodsCode對象。 * **goodsAction.setGoodsId(goodsId)**:用于模擬Struts注入請求參數,即完成數據綁定。 * **goodsAction.buy():**調用goodsAction的buy方法,該方法將委托給IGoodsCodeService實現完成,返回值用于定位視圖。 * **context.assertIsSatisfied():**來驗證前邊定義的預期行為是否執行,且是否正確。 * **Assert.assertEquals(ReflectionTestUtils.getField(goodsAction, "BUY_RESULT"), actualResultCode)**:驗證返回的Result是否是我們指定的。 * **Assert.assertTrue(goodsAction.getActionErrors().size() == 0)**:表示執行Action時沒有錯誤,即Action動作正確。如果條件不成立,說明我們Action功能是錯誤的,需要修改。 通過模擬ActionContext對象內容從而可以非常容易的測試Action中各種與http請求相關情況,無需依賴web服務器即可完成測試。但對于如果我們使用htpp請求相關對象的該如何測試?如果我們需要使用ActionContext獲取值棧數據應該怎么辦?這就需要Struts提供的junit插件支持了。我們會在集成測試中介紹。 對于表現層其他功能的單元測試本書不再介紹,如JSP單元測試、攔截器單元測試等等。 原創內容,轉載請注明私塾在線【[http://sishuok.com/forum/blogPost/list/0/2555.html](http://sishuok.com/forum/blogPost/list/0/2555.html#7330)】
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看