關於單元測試及項目質量管理的總結

摘要:若是沒寫單元測試,如若在branch中對以前代碼重構的話,則沒有移回trunck上的勇氣,有了單元測試,所有運行經過後則有信心合併。互聯網公司更是須要重視單元測試,由於版本迭代比較迅速。所以一個好的單元測試框架及一個好的項目質量管理很是重要。本文便是我對這些的心得體會。
關鍵詞:java, 單元測試, TestNG, DbUnit, Spring, 項目管理, 質量管理, PMP
解決問題:單元測試該如何實施?項目質量管理該如何執行?

 
  在開發數據訪問對象DAO層時,咱們須要直接對數據層進行增刪改查CRUD操做。單元測試很是重要,由於在開發期間常常須要進行代碼重構,怎樣才能保證代碼重構的正確性呢,怎樣給代碼重構者以信心讓他放手去作呢,須要執行單元測試,只要能保證接口功能不發生任何變化,與代碼重構前徹底相同,而且能直觀的感覺到這一一致性,開發者便能大膽地去幹了。功能開發容易,單元測試難作。第一,單元測試間相互影響:單元測試1對數據A新增的一條數據可能會影響到單元測試2驗證的正確性,你可能會想到在每一個單元測試的起始時手動作一遍初始化,好比清理一遍表中數據,以清除其餘單元測試的影響,這樣笨且增長工做量。第二,開發者間相互影響:若是有多人同時須要執行測試用例,使用以上的辦法後仍是會發生問題,這時咱們可能會選擇每一個開發者本身搭建一套數據庫(內存或大型),以免多人間的干擾,這樣仍是過於麻煩,且消耗資源。
 
  下面是基於TestNG和DbUnit的單元測試框架,它的基本思想是管理事務,在單元測試起始時使用事務,在單元測試邏輯的最後將事務回滾,所以每一個單元測試內對數據庫的操做將不會實際對數據庫有實質性影響,這樣在單元測試中既能夠測試邏輯的正確性,又避免影響到了其餘單元測試和其餘開發者,而且只須要依賴於統一的開發數據庫便可,使用還很方便。如下還使用MyBatis的功能抽象出了一個統一平臺,該統一平臺提供了大多數的公共接口,如增刪改查及批量操做等,大部分的基礎操做能夠經過調用這些接口就能完成,不通用的操做傳入SQL語句也可執行。
 
1、MyBatis統一平臺:MyBatisAngelWang.class
  1. @Repository publicclassMyBatisAngelWangimplementsIRepository{ @Autowired privateGeneralDAO generalDAO; public<T extendsBase> T get(Class<T> clz,Long id){ HashMap hashMap = generalDAO.getLogically(clz, id); T ret =this.convert(hashMap, clz); return ret; } }

     

  上面的代碼即對數據庫直接進行了操做,咱們須要對此接口編寫單元測試。具體的MyBatis使用方法,及MyBatisAngelWang統一平臺實現辦法,須要另抽專門章節進行詳細討論。在這裏就不作更深刻研究了。
 
2、統一平臺的單元測試:MyBatisAngelWangTest
  1. import org.testng.annotations.Test; //@DatabaseSetup(value= "/dbunitData/TestAngelEntity.xml")
    publicclassMyBatisAngelWangTestextendsAbstractRollbackTest{ @Autowired privateMyBatisAngelWang myBatisAngelWang; @Test(enabled =false) publicvoid testGet(){ } }

     

  能夠看到此單元測試MyBatisAngelWangTest.class繼承自一個抽象類:AbstractRollbackTest。
  1. import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestExecutionListeners; @ContextConfiguration(locations ={"classpath:spring-datasource-dbunit.xml", "classpath*:spring-services.xml"}) @TestExecutionListeners({DependencyInjectionTestExecutionListener.class, TransactionDbUnitTestExecutionListener.class,TransactionalTestExecutionListener.class}) @Transactional publicclassAbstractRollbackTestextendsAbstractTestNGSpringContextTests{ }

     

  此抽象類是由咱們本身定義的,其繼承自抽象類:AbstractTestNGSpringContextTests,它由Springframework提供。咱們能夠經過ContextConfiguration註解來注入spring配置文件。
 
  或者這樣也能夠。單元測試MyBatisAngelWangTest.class直接繼承自AbstractTestNGSpringContextTests。減小了一層。
  1. @ContextConfiguration("/config/Spring-db.xml") @Transactional @ActiveProfiles("test") publicclassMyBatisAngelWangTestextends AbstractTransactionalTestNGSpringContextTests{}

     

3、供給單元測試的專用spring配置文件:spring-datasource-dbunit.xml
  1. <?xml version="1.0" encoding="UTF-8"?>
    <beansxmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.2.xsd"
    default-autowire="byName">
    <description>spring-datasource-configuration</description>
    <beanclass="com.angel.context.ApplicationContextAwareHelper"/>
    <!-- 定義事務管理器(聲明式的事務) -->
    <beanid="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <propertyname="dataSource"ref="dataSource"/>
    </bean>
    <tx:annotation-driventransaction-manager="transactionManager"/>
    <beanid="propertyConfigurer"class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
    <propertyname="locations">
    <list>
    <value>classpath*:props/datasource_dev.properties</value>
    </list>
    </property>
    </bean>
    <beanid="dataSource"class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <propertyname="driverClassName"value="${jdbc.driver}"/>
    <propertyname="url" value="${jdbc.dbunit.url}"/>
    <propertyname="username"value="${jdbc.user}"/>
    <propertyname="password"value="${jdbc.password}"/>
    </bean>
    <!-- MyBatis 配置 -->
    <beanclass="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <propertyname="basePackage"value="com.angel.*.dao"/>
    <propertyname="sqlSessionFactoryBeanName"value="xSqlSessionFactory"/>
    </bean>
    <beanid="xSqlSessionFactory"class="org.mybatis.spring.SqlSessionFactoryBean">
    <propertyname="dataSource"ref="dataSource"/>
    <propertyname="typeAliasesPackage"value="com.angel.*.entities"/>
    <propertyname="configLocation"value="classpath:mybatis/mybatis-config.xml"/>
    <propertyname="mapperLocations"value="classpath:/com/angel/dao/*.xml"/>
    <propertyname="plugins">
    <array>
    <!--page interceptor-->
    <beanclass="com.angel.orm.db.QueryInterceptor"/>
    </array>
    </property>
    </bean>
    <tx:annotation-driventransaction-manager="transactionManager"/>
    </beans>

       這樣你們測試的數據庫都是同一個了,也不會有任何的相互影響了。由於事務回滾了,不信的話能夠提交一條Insert測試哦,執行完後查看數據庫中並無插入任何數據。然而,在一個單元測試中,先Insert再get是能夠取到數據的,神奇吧?!html

4、其它:ApplicationContextAwareHelper.class
  1. publicclassApplicationContextAwareHelperimplementsApplicationContextAware{ privatestaticApplicationContext context; @Override publicvoid setApplicationContext(ApplicationContext applicationContext){ context = applicationContext; } publicstaticApplicationContext getContext(){ return context; } }

     

當咱們須要比較動態的獲取某些bean時,須要ApplicationContextAwareHelper類。好比說,我要本身拼接一個bean的名稱,還要得到該bean,則可使用下面的代碼來獲取:
  1. DruidDataSource dataSource =ApplicationContextAwareHelper.getBean("dataSource_"+ dataSources[i]);

       固然,這不屬於單元測試的範疇了,有點跑題,可是蠻有用的,在這裏記一下。java

 
5、項目質量管理
 
  經過上面數步就可以很好的實施單元測試了。
然而單元測試說來容易,執行難,有方法了,但推動它又是另一件事了。在互聯網公司中,不少個小項目併發進行,同時存在,項目成員亦流動性較大,相近的項目分佈在各項目組中。這樣,每一個小項目組可能有其本身的規範或是沒有。規範就像法律同樣,是我的素質的最底線、最低層約束。項目開發成員素質較高還好,可能不會引發混亂,當項目組成員多了,素質良莠不齊就麻煩了。這時就須要執行項目經理職能的角色出現了。這時項目經理能夠且應該具體要求各小組的開發流程、規範。在互聯網項目中沒有項目經理存在的狀況下,能夠由行政層面或配置項目管理專員來實現。
  固然項目質量管理除了要規範單元測試之外,還有不少其餘方法,具體能夠查看個人這篇文章:http://www.cnblogs.com/wgp13x/p/4101314.html。其中的B圖-質量管理即體現了項目質量管理的實施辦法,可能不夠細緻,有空再詳細敘述一下。
   多謝你們的鼓勵!
 



相關文章
相關標籤/搜索