集成測試——Spring TestContext框架支持詳細解說

概述

    集成測試是在單元測試之上,一般是將一個或多個已進行過單元測試的組件組合起來完成的,即集成測試中通常不會出現Mock對象,都是實實在在的真實實現。java

    對於單元測試,如前邊在進行數據訪問層單元測試時,經過Mock HibernateTemplate對象而後將其注入到相應的DAO實現,此時單元測試只測試某層的某個功能是否正確,對其餘層如何提供服務採用Mock方式提供。web

    對於集成測試,如要進行數據訪問層集成測試時,須要實實在在的HibernateTemplate對象而後將其注入到相應的DAO實現,此時集成測試將不只測試該層功能是否正確,還將測試服務提供者提供的服務是否正確執行。spring

    使用Spring的一個好處是能很是簡單的進行集成測試,無需依賴web服務器或應用服務器便可完成測試。Spring經過提供一套TestContext框架來簡化集成測試,使用TestContext測試框架能得到許多好處,如Spring IoC容器緩存、事務管理、依賴注入、Spring測試支持類等等。sql

Spring TestContext框架支持

Spring TestContext框架提供了一些通用的集成測試支持,主要提供以下支持:數據庫

1、上下文管理及緩存:緩存

    對於每個測試用例(測試類)應該只有一個上下文,而不是每一個測試方法都建立新的上下文,這樣有助於減小啓動容器的開銷,提供測試效率。可經過以下方式指定要加載的上下文:服務器

  1.  

    @RunWith(SpringJUnit4ClassRunner.class)
    
    @ContextConfiguration(
    
        locations={"classpath:applicationContext-resources-test.xml",
    
                   "classpath:cn/javass/point/dao/applicationContext-hibernate.xml"})
    
    public class GoodsHibernateDaoIntegrationTest {
    
    }

     

  • locations指定Spring配置文件位置;app

  • inheritLocations若是設置爲false,將屏蔽掉父類中使用該註解指定的配置文件位置,默認爲true表示繼承父類中使用該註解指定的配置文件位置。框架

2、Test Fixture(測試固件)的依賴注入:ide

Test Fixture能夠指運行測試時須要的任何東西,通常經過@Before定義的初始化Fixture方法準備這些資源,而經過@After定義的銷燬Fixture方法銷燬或還原這些資源。

Test Fixture的依賴注入就是使用Spring IoC容器的注入功能準備和銷燬這些資源。可經過以下方式注入Test Fixture:

  1.  

    @Autowired
    
    private IGoodsDao goodsDao;
    
    @Autowired
    
    private ApplicationContext ctx;

     

便可以經過Spring提供的註解實現Bean的依賴注入來完成Test Fixture的依賴注入。

3、事務管理: 

開啓測試類的事務管理支持,即便用Spring 容器的事務管理功能,從而能夠獨立於應用服務器完成事務相關功能的測試。爲了使測試中的事務管理起做用須要經過以下方式開啓測試類事務的支持:

  1.  

    @RunWith(SpringJUnit4ClassRunner.class)
    
    @ContextConfiguration(
    
        locations={"classpath:applicationContext-resources-test.xml",
    
                 "classpath:cn/javass/point/dao/applicationContext-hibernate.xml"})
    
    @TransactionConfiguration(
    
    transactionManager = "txManager", defaultRollback=true)
    
    public class GoodsHibernateDaoIntegrationTest {
    
    }

     

Spring提供以下事務相關注解來支持事務管理:

  • @Transactional:使用@Transactional註解的類或方法將獲得事務支持 

  • transactionManager:指定事務管理器;

  • defaultRollback是否回滾事務,默認爲true表示回滾事務。

Spring還經過提供以下註解來簡化事務測試:

  • @Transactional使用@Transactional註解的類或方法表示須要事務支持;

  • @NotTransactional只能註解方法,使用@NotTransactional註解的方法表示不須要事務支持,即不運行在事務中,Spring 3開始已不推薦使用;

  • @BeforeTransaction和@AfterTransaction使用這兩個註解註解的方法定義了在一個事務性測試方法以前或以後執行的行爲,且被註解的方法將運行在該事務性方法的事務以外。

  • @Rollback(true):默認爲true,用於替換@TransactionConfiguration中定義的defaultRollback指定的回滾行爲。

4、經常使用註解支持:Spring框架提供以下註解來簡化集成測試:

  • @DirtiesContext表示每一個測試方法執行完畢需關閉當前上下文並重建一個全新的上下文,即不緩存上下文。可應用到類或方法級別,但在JUnit 3.8中只能應用到方法級別。

  • @ExpectedException表示被註解的方法預期將拋出一個異常,使用如@ExpectedException(NotCodeException.class)來指定異常,定義方式相似於Junit 4中的@Test(expected = NotCodeException.class),@ExpectedException註解和@Test(expected =……)應該二者選一。

  • @Repeat 表示被註解的方法應被重複執行多少次,使用如@Repeat(2)方式指定。

  • @Timed表示被註解的方法必須在多長時間內運行完畢,超時將拋出異常,使用如@Timed(millis=10)方式指定,單位爲毫秒。注意此處指定的時間是以下方法執行時間之和:測試方法執行時間(或者任何測試方法重複執行時間之和)、@Before和@After註解的測試方法以前和以後執行的方法執行時間。而Junit 4中的@Test(timeout=2)指定的超時時間只是測試方法執行時間,不包括任何重複等。

5、TestContext框架支持類:提供對測試框架的支持,如Junit、TestNG測試框架,用於集成Spring TestContext和測試框架來簡化測試,TestContext框架提供以下支持類:

  • JUnit 3.8支持類:提供對Spring TestContext框架與Junit3.8測試框架的集成:

         AbstractJUnit38SpringContextTests咱們的測試類繼承該類後將獲取到Test Fixture的依賴注入好處。

         AbstractTransactionalJUnit38SpringContextTests咱們的測試類繼承該類後除了能獲得Test Fixture的依賴注入好處,還額外獲取到事務管理支持。

  • JUnit 4.5+支持類:提供對Spring TestContext框架與Junit4.5+測試框架的集成:

         AbstractJUnit4SpringContextTests咱們的測試類繼承該類後將獲取到Test Fixture的依賴注入好處。

         AbstractTransactionalJUnit4SpringContextTests咱們的測試類繼承該類後除了能獲得Test Fixture的依賴注入好處,還額外獲取到事務管理支持。

  • 定製 Junit4.5+運行器:經過定製本身的Junit4.5+運行器從而無需繼承JUnit 4.5+支持類便可完成須要的功能,如Test Fixture的依賴注入、事務管理支持,

         @RunWith(SpringJUnit4ClassRunner.class)使用該註解註解到測試類上表示將集成Spring TestContext和Junit 4.5+測試框架。

         @TestExecutionListeners該註解用於指定TestContext框架的監聽器用於與TestContext框架管理器發佈的測試執行事件進行交互,TestContext框架提供以下三個默認的監聽器:DependencyInjectionTestExecutionListener、DirtiesContextTestExecutionListener、TransactionalTestExecutionListener分別完成對Test Fixture的依賴注入、@DirtiesContext支持和事務管理支持,即在默認狀況下將自動註冊這三個監聽器,另外還可使用以下方式指定監聽器:

  1. @RunWith(SpringJUnit4ClassRunner.class)
    
    @TestExecutionListeners({})
    
    public class GoodsHibernateDaoIntegrationTest {
    
    }

     

如上配置將經過定製的Junit4.5+運行器運行,但不會完成Test Fixture的依賴注入、事務管理等等,若是隻須要Test Fixture的依賴注入,可使用@TestExecutionListeners({DependencyInjectionTestExecutionListener.class})指定。

在這裏向你們推薦一個軟件測試學習羣:747981058

準備集成測試環境

    對於集成測試環境各類配置應該和開發環境或實際生產環境配置相分離,即集成測試時應該使用單獨搭建一套獨立的測試環境,不該使用開發環境或實際生產環境的配置,從而保證測試環境、開發、生產環境相分離。

一、拷貝一份Spring資源配置文件applicationContext-resources.xml,並命名爲applicationContext-resources-test.xml表示用於集成測試使用,並修改以下內容:

  1.  

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    
      <property name="locations">
    
            <list>
    
                <value>classpath:resources-test.properties</value>
    
            </list>
    
      </property>
    
    </bean>

     

2、拷貝一份替換配置元數據的資源文件(resources/resources.properties),並命名爲resources-test.properties表示用於集成測試使用,並修改成如下內容:

  1.  

    db.driver.class=org.hsqldb.jdbcDriver
    
    db.url=jdbc:hsqldb:mem:point_shop
    
    db.username=sa
    
    db.password=
    
    #Hibernate屬性
    
    hibernate.dialect=org.hibernate.dialect.HSQLDialect
    
    hibernate.hbm2ddl.auto=create-drop
    
    hibernate.show_sql=false
    
    hibernate.format_sql=true

     

  • jdbc:hsqldb:mem:point_shop咱們在集成測試時將使用HSQLDB,並採用內存數據庫模式運行;

  • hibernate.hbm2ddl.auto=create-drop:表示在建立SessionFactory時根據Hibernate映射配置建立相應Model的表結構,並在SessionFactory關閉時刪除這些表結構。

到此咱們測試環境修改完畢,在進行集成測試時必定要保證測試環境、開發環境、實際生產環境相分離,即對於不一樣的環境使用不一樣的配置文件。

數據訪問層

數據訪問層集成測試,同單元測試同樣目的不只測試該層定義的接口實現方法的行爲是否正確,並且還要測試是否正確與數據庫交互,是否發送並執行了正確的SQL,SQL執行成功後是否正確的組裝了業務邏輯層須要的數據。

數據訪問層集成測試再也不經過Mock對象與數據庫交互的API來完成測試,而是使用實實在在存在的與數據庫交互的對象來完成測試。

接下來讓咱們學習一下如何進行數據訪問層集成測試:

1、在test文件夾下建立以下測試類:

  1.  

    package cn.javass.point.dao.hibernate;
    
    //省略import
    
    @RunWith(SpringJUnit4ClassRunner.class)
    
    @ContextConfiguration(
    
        locations={"classpath:applicationContext-resources-test.xml",
    
                          "classpath:cn/javass/point/dao/applicationContext-hibernate.xml"})
    
    @TransactionConfiguration(transactionManager = "txManager", defaultRollback=false)
    
    public class GoodsHibernateDaoIntegrationTest {
    
        @Autowired
    
        private ApplicationContext ctx;
    
        @Autowired
    
        private IGoodsCodeDao goodsCodeDao;
    
    }

     

  • @RunWith(SpringJUnit4ClassRunner.class)表示使用本身定製的Junit4.5+運行器來運行測試,即完成Spring TestContext框架與Junit集成;

  • @ContextConfiguration指定要加載的Spring配置文件,此處注意咱們的Spring資源配置文件爲「applicationContext-resources-test.xml」;

  • @TransactionConfiguration開啓測試類的事務管理支持配置,並指定事務管理器和默認回滾行爲;

  • @Autowired完成Test Fixture(測試固件)的依賴注入。

2、測試支持寫完後,接下來測試一下分頁查詢全部已發佈的商品是否知足需求:

  1.  

    @Transactional
    
    @Rollback
    
    @Test
    
    public void testListAllPublishedSuccess() {
    
        GoodsModel goods = new GoodsModel();
    
        goods.setDeleted(false);
    
        goods.setDescription("");
    
        goods.setName("測試商品");
    
        goods.setPublished(true);
    
        goodsDao.save(goods);
    
        Assert.assertTrue(goodsDao.listAllPublished(1).size() == 1);
    
        Assert.assertTrue(goodsDao.listAllPublished(2).size() == 0);
    
    }

     

  • @Transactional表示測試方法將容許在事務環境;

  • @Rollback表示替換@ContextConfiguration指定的默認事務回滾行爲,即將在測試方法執行完畢時回滾事務。

數據訪問層的集成測試也是很是簡單,與數據訪問層的單元測試相似,也應該只對複雜的數據訪問層代碼進行測試。

業務邏輯層

業務邏輯層集成測試,目的一樣是測試該層的業務邏輯是否正確,對於數據訪問層實現經過Spring IoC容器完成裝配,即便用真實的數據訪問層實現來獲取相應的底層數據。

 

接下來讓咱們學習一下如何進行業務邏輯層集成測試:

1、在test文件夾下建立以下測試類:

  1.  

    @ContextConfiguration(
    
    locations={"classpath:applicationContext-resources-test.xml",
    
                 "classpath:cn/javass/point/dao/applicationContext-hibernate.xml",
    
                 "classpath:cn/javass/point/service/applicationContext-service.xml"})
    
    @TransactionConfiguration(transactionManager = "txManager", defaultRollback=false)
    
    public class GoodsCodeServiceImplIntegrationTest extends AbstractJUnit4SpringContextTests {
    
        @Autowired
    
        private IGoodsCodeService goodsCodeService;
    
        @Autowired
    
        private IGoodsService goodsService;
    
    }

     

  • AbstractJUnit4SpringContextTests表示將Spring TestContext框架與Junit4.5+測試框架集成;

  • @ContextConfiguration指定要加載的Spring配置文件,此處注意咱們的Spring資源配置文件爲「applicationContext-resources-test.xml」;

  • @TransactionConfiguration開啓測試類的事務管理支持配置,並指定事務管理器和默認回滾行爲;

  • @Autowired完成Test Fixture(測試固件)的依賴注入。

2、測試支持寫完後,接下來測試一下購買商品Code碼是否知足需求:

 

2.1、測試購買失敗的場景:

  1.  

    @Transactional
    
    @Rollback
    
    @ExpectedException(NotCodeException.class)
    
    @Test
    
    public void testBuyFail() {
    
        goodsCodeService.buy("test", 1);
    
    }

     

因爲咱們數據庫中沒有相應商品的Code碼,所以將拋出NotCodeException異常。

2.2、測試購買成功的場景:

  1.  

    @Transactional
    
    @Rollback
    
    @Test
    
    public void testBuySuccess() {
    
        //1.添加商品
    
        GoodsModel goods = new GoodsModel();
    
        goods.setDeleted(false);
    
        goods.setDescription("");
    
        goods.setName("測試商品");
    
        goods.setPublished(true);
    
        goodsService.save(goods);
    
           
    
        //2.添加商品Code碼
    
        GoodsCodeModel goodsCode = new GoodsCodeModel();
    
        goodsCode.setGoods(goods);
    
        goodsCode.setCode("test");
    
        goodsCodeService.save(goodsCode);
    
        //3.測試購買商品Code碼
    
        GoodsCodeModel resultGoodsCode = goodsCodeService.buy("test", 1);
    
        Assert.assertEquals(goodsCode.getId(), resultGoodsCode.getId());
    
    }
    
    在這裏向你們推薦一個軟件測試學習羣:747981058

     

因爲咱們添加了指定商品的Code碼所以購買將成功,若是失敗說明業務寫錯了,應該重寫。

業務邏輯層的集成測試也是很是簡單,與業務邏輯層的單元測試相似,也應該只對複雜的業務邏輯層代碼進行測試。

表現層

對於表現層集成測試,一樣相似於單元測試,但對於業務邏輯層都將使用真實的實現,而再也不是經過Mock對象來測試,這也是集成測試和單元測試的區別。

接下來讓咱們學習一下如何進行表現層Action集成測試:

1、準備Struts提供的junit插件, 到struts-2.2.1.1.zip中拷貝以下jar包到類路徑:

   
 

lib\struts2-junit-plugin-2.2.1.1.jar

2、測試支持類:Struts2提供StrutsSpringTestCase測試支持類,咱們全部的Action測試類都須要繼承該類;

三、準備Spring配置文件:因爲咱們的測試類繼承StrutsSpringTestCase且將經過覆蓋該類的getContextLocations方法來指定Spring配置文件,但因爲getContextLocations方法只能返回一個配置文件,所以咱們須要新建一個用於導入其餘Spring配置文件的配置文件applicationContext-test.xml,具體內容以下:

  1.  

    <import resource="classpath:applicationContext-resources-test.xml"/>
    
    <import resource="classpath:cn/javass/point/dao/applicationContext-hibernate.xml"/>
    
    <import resource="classpath:cn/javass/point/service/applicationContext-service.xml"/>
    
    <import resource="classpath:cn/javass/point/web/pointShop-admin-servlet.xml"/>
    
    <import resource="classpath:cn/javass/point/web/pointShop-front-servlet.xml"/>

     

3.一、在test文件夾下建立以下測試類:

  1.  

    package cn.javass.point.web.front;
    
    //省略import
    
    @RunWith(SpringJUnit4ClassRunner.class)
    
    @TestExecutionListeners({})
    
    public class GoodsActionIntegrationTest extends StrutsSpringTestCase {
    
        @Override
    
        protected String getContextLocations() {
    
            return "classpath:applicationContext-test.xml";
    
        }
    
        @Before
    
        public void setUp() throws Exception {
    
            //1 指定Struts2配置文件
    
            //該方式等價於經過web.xml中的<init-param>方式指定參數
    
            Map<String, String> dispatcherInitParams = new HashMap<String, String>();
    
            ReflectionTestUtils.setField(this, "dispatcherInitParams", dispatcherInitParams);
    
            //1.1 指定Struts配置文件位置
    
            dispatcherInitParams.put("config", "struts-default.xml,struts-plugin.xml,struts.xml");
    
            super.setUp();
    
        }
    
        @After
    
        public void tearDown() throws Exception {
    
            super.tearDown();
    
        }
    
    }

     

  • @RunWith(SpringJUnit4ClassRunner.class)表示使用本身定製的Junit4.5+運行器來運行測試,即完成Spring TestContext框架與Junit集成;

  • @TestExecutionListeners({})沒有指定任何監聽器,即不會自動完成對Test Fixture的依賴注入、@DirtiesContext支持和事務管理支持;

  • StrutsSpringTestCase集成測試Struts2+Spring時全部集成測試類必須繼承該類;

  • setUp方法:在每一個測試方法以前都執行的初始化方法,其中dispatcherInitParams用於指定等價於在web.xml中的<init-param>方式指定的參數;必須調用super.setUp()用於初始化Struts2和Spring環境

  • tearDown()在每一個測試方法以前都執行的銷燬方法,必須調用super.tearDown()來銷燬Spring容器等

4、測試支持寫完後,接下來測試一下前臺購買商品Code碼是否知足需求:

4.1、測試購買失敗的場景:

  1.  

    @Test
    
    public void testBuyFail() throws UnsupportedEncodingException, ServletException {
    
        //2 前臺購買商品失敗
    
        //2.1 首先重置hhtp相關對象,並準備準備請求參數
    
        initServletMockObjects();
    
        request.setParameter("goodsId", String.valueOf(Integer.MIN_VALUE));
    
        //2.2 調用前臺GoodsAction的buy方法完成購買相應商品的Code碼
    
        executeAction("/goods/buy.action");
    
        GoodsAction frontGoodsAction = (GoodsAction) ActionContext.getContext().getActionInvocation().getAction();
    
        //2.3 驗證前臺GoodsAction的buy方法有錯誤
    
        Assert.assertTrue(frontGoodsAction.getActionErrors().size() > 0);
    
    }

     

  • initServletMockObjects():用於重置全部http相關對象,如request等;

  • request.setParameter("goodsId", String.valueOf(Integer.MIN_VALUE)):用於準備請求參數;

  • executeAction("/goods/buy.action")經過模擬http請求來調用前臺GoodsAction的buy方法完成商品購買

  • Assert.assertTrue(frontGoodsAction.getActionErrors().size() > 0):表示執行Action時有錯誤,即Action動做錯誤。若是條件不成立,說明咱們Action功能是錯誤的,須要修改。

4.2、測試購買成功的場景:

  1.  

    @Test
    
    public void testBuySuccess() throws UnsupportedEncodingException, ServletException {
    
        //3 後臺新增商品
    
        //3.1 準備請求參數
    
        request.setParameter("goods.name", "測試商品");
    
        request.setParameter("goods.description", "測試商品描述");
    
        request.setParameter("goods.originalPoint", "1");
    
        request.setParameter("goods.nowPoint", "2");
    
        request.setParameter("goods.published", "true");
    
        //3.2 調用後臺GoodsAction的add方法完成新增
    
        executeAction("/admin/goods/add.action");
    
        //2.3 獲取GoodsAction的goods屬性
    
        GoodsModel goods = (GoodsModel) findValueAfterExecute("goods");
    
        //4 後臺新增商品Code碼
    
        //4.1 首先重置hhtp相關對象,並準備準備請求參數
    
        initServletMockObjects();
    
        request.setParameter("goodsId", String.valueOf(goods.getId()));
    
        request.setParameter("codes", "a\rb");
    
        //4.2 調用後臺GoodsCodeAction的add方法完成新增商品Code碼
    
        executeAction("/admin/goodsCode/add.action");
    
        //5 前臺購買商品成功
    
        //5.1 首先重置hhtp相關對象,並準備準備請求參數
    
        initServletMockObjects();
    
        request.setParameter("goodsId", String.valueOf(goods.getId()));
    
        //5.2 調用前臺GoodsAction的buy方法完成購買相應商品的Code碼
    
        executeAction("/goods/buy.action");
    
        GoodsAction frontGoodsAction = (GoodsAction) ActionContext.getContext().getActionInvocation().getAction();
    
        //5.3 驗證前臺GoodsAction的buy方法沒有錯誤
    
        Assert.assertTrue(frontGoodsAction.getActionErrors().size() == 0);
    
    }

     

  • executeAction("/admin/goods/add.action")調用後臺GoodsAction的add方法,用於新增商品;

  • executeAction("/admin/goodsCode/add.action")調用後臺GoodCodeAction的add方法用於新增商品Code碼;

  • executeAction("/goods/buy.action")調用前臺GoodsAction的buy方法,用於購買相應商品,其中Assert.assertTrue(frontGoodsAction.getActionErrors().size() == 0)表示購買成功,即Action動做正確。

表現層Action集成測試介紹就到此爲止,如何深刻StrutsSpringTestCase來完成集成測試已超出本書範圍,若是讀者對這部分感興趣能夠到Struts2官網學習最新的測試技巧。

結語

 最後跟你們推薦一個學習資料分享羣:747981058,裏面大牛已經爲咱們整理好了許多的學習資料,有自動化,接口,性能等等的學習資料!

人生是一個逆水行舟的過程,不進則退,我們一塊兒加油吧!

相關文章
相關標籤/搜索