欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
使用 Spring 進(jìn)行單元測試

概述

單元測試和集成測試在我們的軟件開(kāi)發(fā)整個(gè)流程中占有舉足輕重的地位,一方面,程序員通過(guò)編寫(xiě)單元測試來(lái)驗證自己程序的有效性,另外一方面,管理者通過(guò)持續自動(dòng)的執行單元測試和分析單元測試的覆蓋率等來(lái)確保軟件本身的質(zhì)量。這里,我們先不談單元測試本身的重要性,對于目前大多數的基于 Java 的企業(yè)應用軟件來(lái)說(shuō),Spring 已經(jīng)成為了標準配置,一方面它實(shí)現了程序之間的低耦合度,另外也通過(guò)一些配置減少了企業(yè)軟件集成的工作量,例如和 Hibernate、Struts 等的集成。那么,有個(gè)問(wèn)題,在普遍使用 Spring 的應用程序中,我們如何去做單元測試?或者說(shuō),我們怎么樣能高效的在 Spring 生態(tài)系統中實(shí)現各種單元測試手段?這就是本文章要告訴大家的事情。

單元測試目前主要的框架包括 Junit、TestNG,還有些 MOCK 框架,例如 Jmock、Easymock、PowerMock 等,這些都是單元測試的利器,但是當把他們用在 Spring 的開(kāi)發(fā)環(huán)境中,還是那么高效么?還好,Spring 提供了單元測試的強大支持,主要特性包括:

  • 支持主流的測試框架 Junit 和 TestNG
  • 支持在測試類(lèi)中使用依賴(lài)注入 Denpendency Injection
  • 支持測試類(lèi)的自動(dòng)化事務(wù)管理
  • 支持使用各種注釋標簽,提高開(kāi)發(fā)效率和代碼簡(jiǎn)潔性
  • Spring 3.1 更是支持在測試類(lèi)中使用非 XML 配置方法和基于 Profile 的 bean 配置模式

通過(guò)閱讀本文,您能夠快速的掌握基于 Spring TestContext 框架的測試方法,并了解基本的實(shí)現原理。本文將提供大量測試標簽的使用方法,通過(guò)這些標簽,開(kāi)發(fā)人員能夠極大的減少編碼工作量。OK,現在讓我們開(kāi)始 Spring 的測試之旅吧!

原來(lái)我們是怎么做的

這里先展示一個(gè)基于 Junit 的單元測試,這個(gè)單元測試運行在基于 Spring 的應用程序中,需要使用 Spring 的相關(guān)配置文件來(lái)進(jìn)行測試。相關(guān)類(lèi)圖如下:

數據庫表

假設有一個(gè)員工賬號表,保存了員工的基本賬號信息,表結構如下:

  • ID:整數類(lèi)型,唯一標識
  • NAME:字符串,登錄賬號
  • SEX:字符串,性別
  • AGE:字符串,年齡

假設表已經(jīng)建好,且內容為空。

測試工程目錄結構和依賴(lài) jar 包

在 Eclipse 中,我們可以展開(kāi)工程目錄結構,看到如下圖所示的工程目錄結構和依賴(lài)的 jar 包列表:

您需要引入的 jar 包括:

  • cglib-nodep-2.2.3.jar
  • commons-logging.jar
  • hsqldb.jar
  • Junit-4.5.jar
  • log4j-1.2.14.jar
  • Spring-asm-3.2.0.M1.jar
  • Spring-beans-3.2.0.M1.jar
  • Spring-context-3.2.0.M1.jar
  • Spring-core-3.2.0.M1.jar
  • Spring-expression-3.2.0.M1.jar
  • Spring-jdbc-3.2.0.M1.jar
  • Spring-test-3.2.0.M1.jar
  • Spring-tx-3.2.0.M1.jar
  • testng-6.8.jar

其中的 hsqldb 是我們測試用數據庫。


圖 1. 工程目錄結構
 

類(lèi)總體介紹

假設我們現在有一個(gè)基于 Spring 的應用程序,除了 MVC 層,還包括業(yè)務(wù)層和數據訪(fǎng)問(wèn)層,業(yè)務(wù)層有一個(gè)類(lèi) AccountService,負責處理賬號類(lèi)的業(yè)務(wù),其依賴(lài)于數據訪(fǎng)問(wèn)層 AccountDao 類(lèi),此類(lèi)提供了基于 Spring Jdbc Template 實(shí)現的數據庫訪(fǎng)問(wèn)方法,AccountService 和 AccountDao 以及他們之間的依賴(lài)關(guān)系都是通過(guò) Spring 配置文件進(jìn)行管理的。

現在我們要對 AccountService 類(lèi)進(jìn)行測試,在不使用 Spring 測試方法之前,我們需要這樣做:


清單 1. Account.Java

此類(lèi)代表賬號的基本信息,提供 getter 和 setter 方法。

 package domain;  public class Account { 	 public static final String SEX_MALE = "male"; 	 public static final String SEX_FEMALE = "female"; 		 private int id; 	 private String name; 	 private int age; 	 private String sex;      public String toString() { 	    return String.format("Account[id=%d,name=%s,age:%d,sex:%s]",id,name,age,sex); 	 } 	 public int getId() { 		 return id; 	 } 	 public void setId(int id) { 		 this.id = id; 	 } 	 public String getName() { 		 return name; 	 } 	 public void setName(String name) { 		 this.name = name; 	 } 	 public int getAge() { 		 return age; 	 } 	 public void setAge(int age) { 		 this.age = age; 	 } 	 public String getSex() { 		 return sex; 	 } 	 public void setSex(String sex) { 		 this.sex = sex; 	 } 	     public static Account getAccount(int id,String name,int age,String sex) { 		 Account acct = new Account(); 		 acct.setId(id); 		 acct.setName(name); 		 acct.setAge(age); 		 acct.setSex(sex); 		 return acct; 	 }  } 

注意上面的 Account 類(lèi)有一個(gè) toString() 方法和一個(gè)靜態(tài)的 getAccount 方法,getAccount 方法用于快速獲取 Account 測試對象。


清單 2. AccountDao.Java

這個(gè) DAO 我們這里為了簡(jiǎn)單起見(jiàn),采用 Spring Jdbc Template 來(lái)實(shí)現。

 package DAO;  import Java.sql.ResultSet;  import Java.sql.SQLException;  import Java.util.HashMap;  import Java.util.List;  import Java.util.Map;  import org.Springframework.context.ApplicationContext;  import org.Springframework.context.support.ClassPathXmlApplicationContext;  import org.Springframework.jdbc.core.RowMapper;  import org.Springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;  import org.Springframework.jdbc.core.simple.ParameterizedRowMapper;  import domain.Account;  public class AccountDao extends NamedParameterJdbcDaoSupport { 	 public void saveAccount(Account account) { 		 String sql = "insert into tbl_account(id,name,age,sex) " + 				"values(:id,:name,:age,:sex)"; 		 Map paramMap = new HashMap(); 		 paramMap.put("id", account.getId()); 		 paramMap.put("name", account.getName()); 		 paramMap.put("age", account.getAge()); 		 paramMap.put("sex",account.getSex()); 		 getNamedParameterJdbcTemplate().update(sql, paramMap); 	 } 		 public Account getAccountById(int id) { 		 String sql = "select id,name,age,sex from tbl_account where id=:id"; 		 Map paramMap = new HashMap(); 		 paramMap.put("id", id); 		 List<Account> matches = getNamedParameterJdbcTemplate().query(sql, 		 paramMap,new ParameterizedRowMapper<Account>() { 					 @Override 					 public Account mapRow(ResultSet rs, int rowNum) 							 throws SQLException { 						 Account a = new Account(); 						 a.setId(rs.getInt(1)); 						 a.setName(rs.getString(2)); 						 a.setAge(rs.getInt(3)); 						 a.setSex(rs.getString(4)); 						 return a; 					 } 					 }); 		 return matches.size()>0?matches.get(0):null; 	 } 	 } 

AccountDao 定義了幾個(gè)賬號對象的數據庫訪(fǎng)問(wèn)方法:

  • saveAccount:負責把傳入的賬號對象入庫
  • getAccountById:負責根據 Id 查詢(xún)賬號

清單 3. AccountService.Java
 package service;  import org.apache.commons.logging.Log;  import org.apache.commons.logging.LogFactory;  import org.Springframework.beans.factory.annotation.Autowired;  import DAO.AccountDao;  import domain.Account;  public class AccountService { 	 private static final Log log = LogFactory.getLog(AccountService.class); 		 @Autowired 	 private AccountDao accountDao; 		 public Account getAccountById(int id) { 		 return accountDao.getAccountById(id); 	 } 		 public void insertIfNotExist(Account account) { 		 Account acct = accountDao.getAccountById(account.getId()); 		 if(acct==null) { 			 log.debug("No "+account+" found,would insert it.");              accountDao.saveAccount(account); 		 } 		 acct = null; 	 } 		 } 

AccountService 包括下列方法:

  • getAccountById:根據 Id 查詢(xún)賬號信息
  • insertIfNotExist:根據傳入的對象插入數據庫

其依賴(lài)的 DAO 對象 accountDao 是通過(guò) Spring 注釋標簽 @Autowired 自動(dòng)注入的。


清單 4. Spring 配置文件

上述幾個(gè)類(lèi)的依賴(lài)關(guān)系是通過(guò) Spring 進(jìn)行管理的,配置文件如下:

 <beans xmlns="http://www.Springframework.org/schema/beans"	 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.Springframework.org/schema/context" xsi:schemaLocation="http://www.Springframework.org/schema/beans  http://www.Springframework.org/schema/beans/Spring-beans-3.0.xsd  http://www.Springframework.org/schema/context  http://www.Springframework.org/schema/context/Spring-context-3.0.xsd "> 	 <context:annotation-config/>  <bean id="datasource" class=" org.Springframework.jdbc.datasource.DriverManagerDataSource"> 		 <property name="driverClassName" value="org.hsqldb.jdbcDriver" /> 		 <property name="url" value="jdbc:hsqldb:hsql://localhost" /> 		 <property name="username" value="sa" /> 		 <property name="password" value="" /> 	 </bean> 	 <bean id="initer" init-method="init" class="service.Initializer"> 	 </bean>  <bean id="accountDao" depends-on="initer" class="DAO.AccountDao"> 		 <property name="dataSource" ref="datasource" /> 	 </bean>  <bean id="accountService" class="service.AccountService"> 	 </bean>  </beans> 

注意其中的“<context:annotation-config/>”的作用,這個(gè)配置啟用了 Spring 對 Annotation 的支持,這樣在我們的測試類(lèi)中 @Autowired 注釋才會(huì )起作用(如果用了 Spring 測試框架,則不需要這樣的配置項,稍后會(huì )演示)。另外還有一個(gè) accountDao 依賴(lài)的 initer bean, 這個(gè) bean 的作用是加載 log4j 日志環(huán)境,不是必須的。

另外還有一個(gè)要注意的地方,就是 datasource 的定義,由于我們使用的是 Spring Jdbc Template,所以只要定義一個(gè) org.Springframework.jdbc.datasource.DriverManagerDataSource 類(lèi)型的 datasource 即可。這里我們使用了簡(jiǎn)單的數據庫 HSQL、Single Server 運行模式,通過(guò) JDBC 進(jìn)行訪(fǎng)問(wèn)。實(shí)際測試中,大家可以選擇 Oracle 或者 DB2、Mysql 等。

好,萬(wàn)事具備,下面我們來(lái)用 Junit4 框架測試 accountService 類(lèi)。代碼如下:


清單 5. AccountServiceOldTest.Java
 package service;  import static org.Junit.Assert.assertEquals;  import org.Junit.BeforeClass;  import org.Junit.Test;  import org.Springframework.context.ApplicationContext;  import org.Springframework.context.support.ClassPathXmlApplicationContext;  import domain.Account;  public class AccountServiceOldTest { 	 private static AccountService service; 		 @BeforeClass 	 public static void init() { 		 ApplicationContext  context = new ClassPathXmlApplicationContext("config/Spring-db-old.xml"); 		 service = (AccountService)context.getBean("accountService"); 	 } 			 @Test 	 public void testGetAcccountById() {  Account acct = Account.getAccount(1, "user01", 18, "M"); 		 Account acct2 = null; 		 try {  service.insertIfNotExist(acct); 			 acct2 = service.getAccountById(1); 			 assertEquals(acct, acct2); 		 } catch (Exception ex) { 			 fail(ex.getMessage()); 		 } finally { 			 service.removeAccount(acct); 		 }  }  } 

注意上面的 Junit4 注釋標簽,第一個(gè)注釋標簽 @BeforeClass,用來(lái)執行整個(gè)測試類(lèi)需要一次性初始化的環(huán)境,這里我們用 Spring 的 ClassPathXmlApplicationContext 從 XML 文件中加載了上面定義的 Spring 配置文件,并從中獲得了 accountService 的實(shí)例。第二個(gè)注釋標簽 @Test 用來(lái)進(jìn)行實(shí)際的測試。

測試過(guò)程:我們先獲取一個(gè) Account 實(shí)例對象,然后通過(guò) service bean 插入數據庫中,然后通過(guò) getAccountById 方法從數據庫再查詢(xún)這個(gè)記錄,如果能獲取,則判斷兩者的相等性;如果相同,則表示測試成功。成功后,我們嘗試刪除這個(gè)記錄,以利于下一個(gè)測試的進(jìn)行,這里我們用了 try-catch-finally 來(lái)保證賬號信息會(huì )被清除。

執行測試:(在 Eclipse 中,右鍵選擇 AccountServiceOldTest 類(lèi),點(diǎn)擊 Run as Junit test 選項),得到的結果如下:

執行測試的結果

在 Eclipse 的 Junit 視圖中,我們可以看到如下的結果:


圖 2. 測試的結果
 

對于這種不使用 Spring test 框架進(jìn)行的單元測試,我們注意到,需要做這些工作:

  • 在測試開(kāi)始之前,需要手工加載 Spring 的配置文件,并獲取需要的 bean 實(shí)例
  • 在測試結束的時(shí)候,需要手工清空搭建的數據庫環(huán)境,比如清除您插入或者更新的數據,以保證對下一個(gè)測試沒(méi)有影響

另外,在這個(gè)測試類(lèi)中,我們還不能使用 Spring 的依賴(lài)注入特性。一切都靠手工編碼實(shí)現。好,那么我們看看 Spring test 框架能做到什么。

首先我們修改一下 Spring 的 XML 配置文件,刪除 <context:annotation-config/> 行,其他不變。


清單 6. Spring-db1.xml
 <beans xmlns="http://www.Springframework.org/schema/beans"	 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.Springframework.org/schema/beans  http://www.Springframework.org/schema/beans/Spring-beans-3.2.xsd">  <bean id="datasource"  class="org.Springframework.jdbc.datasource.DriverManagerDataSource"> 		 <property name="driverClassName" value="org.hsqldb.jdbcDriver" /> 		 <property name="url" value="jdbc:hsqldb:hsql://localhost" /> 		 <property name="username" value="sa"/> 		 <property name="password" value=""/> 	 </bean>  <bean id="transactionManager"  class="org.Springframework.jdbc.datasource.DataSourceTransactionManager">  		 <property name="dataSource" ref="datasource"></property>  	 </bean>  	 <bean id="initer" init-method="init" class="service.Initializer">  	 </bean>  <bean id="accountDao" depends-on="initer" class="DAO.AccountDao">  		 <property name="dataSource" ref="datasource"/>  	 </bean>  	 <bean id="accountService" class="service.AccountService">  	 </bean>  </beans> 

其中的 transactionManager 是 Spring test 框架用來(lái)做事務(wù)管理的管理器。


清單 7. AccountServiceTest1.Java
 package service;  import static org.Junit.Assert.assertEquals;  import org.Junit.Test;  import org.Junit.runner.RunWith;  import org.Springframework.beans.factory.annotation.Autowired;  import org.Springframework.test.context.ContextConfiguration;  import org.Springframework.test.context.Junit4.SpringJUnit4ClassRunner;  import org.Springframework.transaction.annotation.Transactional;  import domain.Account;  @RunWith(SpringJUnit4ClassRunner.class)  @ContextConfiguration("/config/Spring-db1.xml")  @Transactional  public class AccountServiceTest1 { 	 @Autowired 	 private AccountService service; 		 @Test 	 public void testGetAcccountById() {  Account acct = Account.getAccount(1, "user01", 18, "M"); 		 service.insertIfNotExist(acct); 		 Account acct2 = service.getAccountById(1); 		 assertEquals(acct,acct2); 	 }  } 

對這個(gè)類(lèi)解釋一下:

  • @RunWith 注釋標簽是 Junit 提供的,用來(lái)說(shuō)明此測試類(lèi)的運行者,這里用了 SpringJUnit4ClassRunner,這個(gè)類(lèi)是一個(gè)針對 Junit 運行環(huán)境的自定義擴展,用來(lái)標準化在 Spring 環(huán)境中 Junit4.5 的測試用例,例如支持的注釋標簽的標準化
  • @ContextConfiguration 注釋標簽是 Spring test context 提供的,用來(lái)指定 Spring 配置信息的來(lái)源,支持指定 XML 文件位置或者 Spring 配置類(lèi)名,這里我們指定 classpath 下的 /config/Spring-db1.xml 為配置文件的位置
  • @Transactional 注釋標簽是表明此測試類(lèi)的事務(wù)啟用,這樣所有的測試方案都會(huì )自動(dòng)的 rollback,即您不用自己清除自己所做的任何對數據庫的變更了
  • @Autowired 體現了我們的測試類(lèi)也是在 Spring 的容器中管理的,他可以獲取容器的 bean 的注入,您不用自己手工獲取要測試的 bean 實(shí)例了
  • testGetAccountById 是我們的測試用例:注意和上面的 AccountServiceOldTest 中相同的測試方法的對比,這里我們不用再 try-catch-finally 了,事務(wù)管理自動(dòng)運行,當我們執行完成后,所有相關(guān)變更會(huì )被自動(dòng)清除

執行結果

在 Eclipse 的 Junit 視圖中,我們可以看到如下的結果:


圖 3. 執行結果
 

小結

如果您希望在 Spring 環(huán)境中進(jìn)行單元測試,那么可以做如下配置:

  • 繼續使用 Junit4 測試框架,包括其 @Test 注釋標簽和相關(guān)的類(lèi)和方法的定義,這些都不用變
  • 您需要通過(guò) @RunWith(SpringJUnit4ClassRunner.class) 來(lái)啟動(dòng) Spring 對測試類(lèi)的支持
  • 您需要通過(guò) @ContextConfiguration 注釋標簽來(lái)指定 Spring 配置文件或者配置類(lèi)的位置
  • 您需要通過(guò) @Transactional 來(lái)啟用自動(dòng)的事務(wù)管理
  • 您可以使用 @Autowired 自動(dòng)織入 Spring 的 bean 用來(lái)測試

另外您不再需要:

  • 手工加載 Spring 的配置文件
  • 手工清理數據庫的每次變更
  • 手工獲取 application context 然后獲取 bean 實(shí)例

Spring 測試注釋標簽

我們已經(jīng)看到利用 Spring test framework 來(lái)進(jìn)行基于 Junit4 的單元測試是多么的簡(jiǎn)單,下面我們來(lái)看一下前面遇到的各種注釋標簽的一些可選用法。

@ContextConfiguration 和 @Configuration 的使用

剛才已經(jīng)介紹過(guò),可以輸入 Spring xml 文件的位置,Spring test framework 會(huì )自動(dòng)加載 XML 文件,得到 application context,當然也可以使用 Spring 3.0 新提供的特性 @Configuration,這個(gè)注釋標簽允許您用 Java 語(yǔ)言來(lái)定義 bean 實(shí)例,舉個(gè)例子:

現在我們將前面定義的 Spring-db1.xml 進(jìn)行修改,我們希望其中的三個(gè) bean:initer、accountDao、accountService 通過(guò)配置類(lèi)來(lái)定義,而不是 XML,則我們需要定義如下配置類(lèi):

注意:如果您想使用 @Configuration,請在 classpath 中加入 cglib 的 jar 包(cglib-nodep-2.2.3.jar),否則會(huì )報錯。


清單 8. SpringDb2Config.Java
 package config;  import org.Springframework.beans.factory.annotation.Autowired;  import org.Springframework.context.annotation.Bean;  import org.Springframework.context.annotation.Configuration;  import org.Springframework.jdbc.datasource.DriverManagerDataSource;  import service.AccountService;  import service.Initializer;  import DAO.AccountDao;  @Configuration  public class SpringDb2Config { 	 private @Autowired DriverManagerDataSource datasource; 	 @Bean 	 public Initializer initer() { 		 return new Initializer(); 	 } 		 @Bean 	 public AccountDao accountDao() {  AccountDao DAO = new AccountDao();  DAO.setDataSource(datasource);  return DAO; 	 } 		 @Bean 	 public AccountService accountService() {  return new AccountService(); 	 }  } 

注意上面的注釋標簽:

  • @Configuration:表明這個(gè)類(lèi)是一個(gè) Spring 配置類(lèi),提供 Spring 的 bean 定義,實(shí)際效果等同于 XML 配置方法
  • @Bean:表明這個(gè)方法是一個(gè) bean 的定義,缺省情況下,方法名稱(chēng)就是 bean 的 Id
  • @Autowired:這個(gè) datasource 采用自動(dòng)注入的方式獲取

注意,我們采用的是 XML+config bean 的方式進(jìn)行配置,這種方式比較符合實(shí)際項目的情況。相關(guān)的 Spring 配置文件也要做變化,如下清單所示:


清單 9. Spring-db2.xml
 <beans xmlns="http://www.Springframework.org/schema/beans"	 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.Springframework.org/schema/context" xsi:schemaLocation="http://www.Springframework.org/schema/beans  http://www.Springframework.org/schema/beans/Spring-beans-3.0.xsd  http://www.Springframework.org/schema/context  http://www.Springframework.org/schema/context/Spring-context-3.0.xsd">  <context:annotation-config/>  <bean id="datasource"  class="org.Springframework.jdbc.datasource.DriverManagerDataSource"> 		 <property name="driverClassName" value="org.hsqldb.jdbcDriver" /> 		 <property name="url" value="jdbc:hsqldb:hsql://localhost" /> 		 <property name="username" value="sa"/> 		 <property name="password" value=""/> 	 </bean>  <bean id="transactionManager"           class="org.Springframework.jdbc.datasource.DataSourceTransactionManager">  		 <property name="dataSource" ref="datasource"></property>  	 </bean>  	 	 <bean class="config.SpringDb2Config"/>  </beans> 

注意里面的 context 命名空間的定義,如代碼中黑體字所示。另外還必須有 <context:annotaiton-config/> 的定義,這個(gè)定義允許采用注釋標簽的方式來(lái)控制 Spring 的容器,最后我們看到 beans 已經(jīng)沒(méi)有 initer、accountDao 和 accountService 這些 bean 的定義,取而代之的是一個(gè) SpringDb2Config bean 的定義,注意這個(gè) bean 沒(méi)有名稱(chēng),因為不需要被引用。

現在有了這些配置,我們的測試類(lèi)只要稍稍修改一下,即可實(shí)現加載配置類(lèi)的效果,如下:

 @ContextConfiguration("/config/Spring-db2.xml") 

通過(guò)上面的配置,測試用例就可以實(shí)現加載 Spring 配置類(lèi),運行結果也是成功的 green bar。

@DirtiesContext

缺省情況下,Spring 測試框架一旦加載 applicationContext 后,將一直緩存,不會(huì )改變,但是,

由于 Spring 允許在運行期修改 applicationContext 的定義,例如在運行期獲取 applicationContext,然后調用 registerSingleton 方法來(lái)動(dòng)態(tài)的注冊新的 bean,這樣的情況下,如果我們還使用 Spring 測試框架的被修改過(guò) applicationContext,則會(huì )帶來(lái)測試問(wèn)題,我們必須能夠在運行期重新加載 applicationContext,這個(gè)時(shí)候,我們可以在測試類(lèi)或者方法上注釋?zhuān)篅DirtiesContext,作用如下:

  • 如果定義在類(lèi)上(缺?。?,則在此測試類(lèi)運行完成后,重新加載 applicationContext
  • 如果定義在方法上,即表示測試方法運行完成后,重新加載 applicationContext

@TransactionConfiguration 和 @Rollback

缺省情況下,Spring 測試框架將事務(wù)管理委托到名為 transactionManager 的 bean 上,如果您的事務(wù)管理器不是這個(gè)名字,那需要指定 transactionManager 屬性名稱(chēng),還可以指定 defaultRollback 屬性,缺省為 true,即所有的方法都 rollback,您可以指定為 false,這樣,在一些需要 rollback 的方法,指定注釋標簽 @Rollback(true)即可。

對 Junit4 的注釋標簽支持

看了上面 Spring 測試框架的注釋標簽,我們來(lái)看看一些常見(jiàn)的基于 Junit4 的注釋標簽在 Spring 測試環(huán)境中的使用方法。

@Test(expected=...)

此注釋標簽的含義是,這是一個(gè)測試,期待一個(gè)異常的發(fā)生,期待的異常通過(guò) xxx.class 標識。例如,我們修改 AccountService.Java 的 insertIfNotExist 方法,對于傳入的參數如果為空,則拋出 IllegalArgumentException,如下:

 public void insertIfNotExist(Account account) { 	 if(account==null) 		 throw new IllegalArgumentException("account is null"); 	 Account acct = accountDao.getAccountById(account.getId()); 	 if(acct==null) { 		 log.debug("No "+account+" found,would insert it."); 		 accountDao.saveAccount(account); 	 } 	 acct = null;  } 

然后,在測試類(lèi)中增加一個(gè)測試異常的方法,如下:

 @Test(expected=IllegalArgumentException.class)  public void testInsertException() { 	 service.insertIfNotExist(null);  } 

運行結果是 green bar。

@Test(timeout=...)

可以給測試方法指定超時(shí)時(shí)間(毫秒級別),當測試方法的執行時(shí)間超過(guò)此值,則失敗。

比如在 AccountService 中增加如下方法:

 public void doSomeHugeJob() { 	 try { 		 Thread.sleep(2*1000); 	 } catch (InterruptedException e) { 	 }  } 

上述方法模擬任務(wù)執行時(shí)間 2 秒,則測試方法如下:

 @Test(timeout=3000)  public void testHugeJob() { 	 service.doSomeHugeJob();  } 

上述測試方法期待 service.doSomeHugeJob 方法能在 3 秒內結束,執行測試結果是 green bar。

@Repeat

通過(guò) @Repeat,您可以輕松的多次執行測試用例,而不用自己寫(xiě) for 循環(huán),使用方法:

 @Repeat(3)  @Test(expected=IllegalArgumentException.class)  public void testInsertException() { 	 service.insertIfNotExist(null);  } 

這樣,testInsertException 就能被執行 3 次。

在測試類(lèi)中基于 profile 加載測試 bean

從 Spring 3.2 以后,Spring 開(kāi)始支持使用 @ActiveProfiles 來(lái)指定測試類(lèi)加載的配置包,比如您的配置文件只有一個(gè),但是需要兼容生產(chǎn)環(huán)境的配置和單元測試的配置,那么您可以使用 profile 的方式來(lái)定義 beans,如下:


清單 10. Spring-db.xml
 <beans xmlns="http://www.Springframework.org/schema/beans"	 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.Springframework.org/schema/beans  http://www.Springframework.org/schema/beans/Spring-beans-3.2.xsd">  	 <beans profile="test">  <bean id="datasource"  class="org.Springframework.jdbc.datasource.DriverManagerDataSource">  <property name="driverClassName" value="org.hsqldb.jdbcDriver" /> 	 <property name="url" value="jdbc:hsqldb:hsql://localhost" /> 	 <property name="username" value="sa"/> 	 <property name="password" value=""/> 	 </bean> </beans> 	<beans profile="production">  <bean id="datasource"  class="org.Springframework.jdbc.datasource.DriverManagerDataSource"> 	 <property name="driverClassName" value="org.hsqldb.jdbcDriver" /> 	 <property name="url" value="jdbc:hsqldb:hsql://localhost/prod" /> 	 <property name="username" value="sa"/> 	 <property name="password" value=""/> 	 </bean>  </beans>  <beans profile="test,production">  <bean id="transactionManager"      class="org.Springframework.jdbc.datasource.DataSourceTransactionManager"> 	 <property name="dataSource" ref="datasource"></property> 	 </bean> 	 <bean id="initer" init-method="init" class="service.Initializer"> 	 </bean>  <bean id="accountDao" depends-on="initer" class="DAO.AccountDao"> 	 		 <property name="dataSource" ref="datasource"/> 	 	 </bean> 	 		 	 <bean id="accountService" class="service.AccountService"> 	 	 </bean> 	 	 <bean id="envSetter" class="EnvSetter"/>  	 </beans>  </beans> 

上面的定義,我們看到:

  • 在 XML 頭中我們引用了 Spring 3.2 的 beans 定義,因為只有 Spring 3.2+ 才支持基于 profile 的定義
  • 在 <beans> 根節點(diǎn)下可以嵌套 <beans> 定義,要指定 profile 屬性,這個(gè)配置中,我們定義了兩個(gè) datasource,一個(gè)屬于 test profile,一個(gè)輸入 production profile,這樣,我們就能在測試程序中加載 test profile,不影響 production 數據庫了
  • 在下面定義了一些屬于兩個(gè) profile 的 beans,即 <beans profile=”test,production”> 這樣方便重用一些 bean 的定義,因為這些 bean 在兩個(gè) profile 中都是一樣的

清單 11. AccountServiceTest.Java
 @RunWith(SpringJUnit4ClassRunner.class)  @ContextConfiguration("/config/Spring-db.xml")  @Transactional  @ActiveProfiles("test")  public class AccountServiceTest {  ...  } 

注意上面的 @ActiveProfiles,可以指定一個(gè)或者多個(gè) profile,這樣我們的測試類(lèi)就僅僅加載這些名字的 profile 中定義的 bean 實(shí)例。

對 TestNG 的支持

Spring 2.5 以后,就開(kāi)始支持 TestNG 了,支持的方法包括:

  • 將您的 TestNG 測試類(lèi)繼承 Spring 的測試父類(lèi):AbstractTransactionalTestNGSpringContextTests 或者 AbstractTestNGSpringContextTests,這樣您的 TestNG 測試類(lèi)內部就可以訪(fǎng)問(wèn) applicationContext 成員變量了
  • 不繼承 Spring 父類(lèi),在測試類(lèi)上使用 @TestExecutionListeners 注釋標簽,可以引入的監聽(tīng)器包括
    • DependencyInjectionTestExecutionListener:使得測試類(lèi)擁有依賴(lài)注入特性
    • DirtiesContextTestExecutionListener:使得測試類(lèi)擁有更新 applicationContext 能力
    • TransactionalTestExecutionListener:使得測試類(lèi)擁有自動(dòng)的事務(wù)管理能力

這里我們演示一下如何使用 Spring 提供的 TestNG 父類(lèi)來(lái)進(jìn)行測試。


清單 12. AccountServiceTestNGTest.Java
 package testng;  import static org.Junit.Assert.assertEquals;  import org.Springframework.beans.factory.annotation.Autowired;  import org.Springframework.test.context.ActiveProfiles;  import org.Springframework.test.context.ContextConfiguration;  import org.Springframework.test.context.testng.  AbstractTransactionalTestNGSpringContextTests;  import org.Springframework.transaction.annotation.Transactional;  import service.AccountService;  import domain.Account;  @ContextConfiguration("/config/Spring-db.xml")  @Transactional  @ActiveProfiles("test")  public class AccountServiceTestNGTest extends  AbstractTransactionalTestNGSpringContextTests { 	 @Autowired 	 private AccountService service; 		 @org.testng.annotations.Test 	 public void testGetAcccountById() { 		 Account acct = Account.getAccount(1, "user01", 18, "M"); 		 service.insertIfNotExist(acct); 		 Account acct2 = service.getAccountById(1); 		 assertEquals(acct,acct2); 	 }  } 

執行測試,我們將看到測試成功。


圖 4. 測試成功
 

搜索數據庫對應的表,我們看到里面沒(méi)有數據,說(shuō)明自動(dòng)事務(wù)起作用了。

基本原理

Spring test framework 主要位于 org.Springframework.test.context 包中,主要包括下面幾個(gè)類(lèi):


圖 5. Spring 測試框架類(lèi)圖(查看大圖
 
  • TestContextManager:主要的入口類(lèi),提供 TestContext 實(shí)例的管理,負責根據各種事件來(lái)通知測試監聽(tīng)器
  • TestContext:實(shí)體類(lèi),提供訪(fǎng)問(wèn) Spring applicatoin context 的能力,并負責緩存 applicationContext
  • TestExecutionListener:測試監聽(tīng)器,提供依賴(lài)注入、applicationContext 緩存和事務(wù)管理等能力
  • ContextLoader:負責根據配置加載 Spring 的 bean 定義,以構建 applicationContext 實(shí)例對象
  • SmartContextLoader:Spring 3.1 引入的新加載方法,支持按照 profile 加載

Spring 通過(guò) AOP hook 了測試類(lèi)的實(shí)例創(chuàng )建、beforeClass、before、after、afterClass 等事件入口,執行順序主要如下:


圖 6. Spring 測試框架執行序列圖(查看大圖
 
  • 測試執行者開(kāi)始執行測試類(lèi),這個(gè)時(shí)候 Spring 獲取消息,自動(dòng)創(chuàng )建 TestContextManager 實(shí)例
  • TestContextManager 會(huì )創(chuàng )建 TestContext,以記錄當前測試的上下文信息,TestContext 則通過(guò) ContextLoader 來(lái)獲取 Spring ApplicationContext 實(shí)例
  • 當測試執行者開(kāi)始執行測試類(lèi)的 BeforeClass、Before、After、AfterClass 的時(shí)候,TestContextManager 將截獲事件,發(fā)通知給對應的 TestExecutionListener

總結

根據上面的例子和介紹,我們可以看到,Spring 測試框架的主要特點(diǎn)如下:

  • 完美的支持了 Junit4(提供特別的 SpringJunit4ClassRunner),比較好的支持了 TestNG
  • 在支持原有單元測試能力的基礎上,通過(guò)各種監聽(tīng)器,支持了測試類(lèi)的依賴(lài)注入、對 Spring applicationContext 的訪(fǎng)問(wèn)以及事務(wù)管理能力,為使用 Spring 架構的應用程序的測試帶來(lái)了極大的便利性
  • Spring 3.1 引入的基于 profile 的加載能力使得測試環(huán)境和正式環(huán)境可以在一個(gè) XML 定義中完美的結合

總之,如果您的程序中使用了 Spring,且對用 Junit 或者 testNG 來(lái)對他們進(jìn)行單元測試感到力不從心,可以考慮使用 Spring test framework,它將使您的應用程序的質(zhì)量上一個(gè)新的臺階。

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
Spring boot Mybatis(讀寫(xiě)分離配置)
在spring配置jdbc
工作流Activiti的學(xué)習總結(四)Spring和Activiti的整合配置講解
(四)spring+servlet 整合
MyBatis3整合Spring3_SpringMVC
使用 Spring 2.5 TestContext 測試框架
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久