?
This document uses PHP Chinese website manual Release
能夠無(wú)需部署到你的應(yīng)用服務(wù)器上或連接其它企業(yè)架構(gòu)就實(shí)現(xiàn)集成測(cè)試是非常重要的。這可以讓你來(lái)進(jìn)行以下測(cè)試:
正確配置Spring IoC 容器上下文。
使用JDBC或ORM工具的數(shù)據(jù)訪問(wèn)??赡馨ㄈ鏢QL腳本,Hibernate query,JPA 實(shí)體映射等的正確性驗(yàn)證。
Spring框架提供集成測(cè)試的一流支持,相關(guān)類(lèi)打包在spring-test.jar
類(lèi)庫(kù)中。在這個(gè)類(lèi)庫(kù)中,你可以找到org.springframework.test
包,有很多方便使用Spring容器進(jìn)行集成測(cè)試的類(lèi),而且同時(shí)不依賴(lài)應(yīng)用服務(wù)器或其它部署環(huán)境。這些測(cè)試會(huì)比單元測(cè)試慢,但會(huì)比Cactus(譯者注:Apache測(cè)試服務(wù)端Java代碼的工具h(yuǎn)ttp://jakarta.apache.org/cactus/index.html )測(cè)試或依靠部署到一個(gè)應(yīng)用服務(wù)器上來(lái)進(jìn)行遠(yuǎn)程測(cè)試要快捷的多。
在2.5版本之前,Spring已經(jīng)提供了面向JUnit 3.8的單元測(cè)試支持. 在2.5版本中, Spring 提供了單元和集成測(cè)試支持 Spring TestContext框架。 它是實(shí)際測(cè)試框架的混合體,因此能夠幫助在多個(gè)測(cè)試環(huán)境包括JUnit 3.8,JUnit 4.4, TestNG等中進(jìn)行測(cè)試。 注意Spring TestContext框架需要Java 5+支持.
Spring團(tuán)隊(duì)推薦使用Spring TestContext框架
來(lái)進(jìn)行所有新的單元測(cè)試和集成測(cè)試,以包括ApplicationContext
或需要事務(wù)管理的情況。
但如果你開(kāi)發(fā)在Java5之前的環(huán)境上,就需要繼續(xù)使用JUnit 3.8遺留支持. 另外,顯式
JPA集成測(cè)試支持
依賴(lài)于shadow class載入來(lái)進(jìn)行JPA類(lèi)測(cè)試(class instrumentation)目前只能與JUnit 3.8遺留支持相容。
如果你要測(cè)試的JPA提供者不需要class instrumentation,就推薦使用TestContext框架。
Spring集成測(cè)試支持框架提供了一些通用目標(biāo),包括:
跨越各個(gè)測(cè)試案例執(zhí)行期的Spring IoC容器緩存。
測(cè)試fixture實(shí)例的依賴(lài)注入 (這很爽)。
適合集成測(cè)試的事務(wù)管理(這更加爽)。
Spring特有的支持類(lèi)在編寫(xiě)集成測(cè)試時(shí)真的很有用。
下面的章節(jié)具體描述每一個(gè)目標(biāo)并提供指向特定支持框架的信息的鏈接。
Spring集成測(cè)試支持框架提供了ApplicationContext
的持久化載入和這些上下文的緩存機(jī)制。
對(duì)已載入上下文的緩存是很重要的,因?yàn)槿绻闶窃谝粋€(gè)大型的項(xiàng)目中,啟動(dòng)時(shí)間會(huì)成為一個(gè)問(wèn)題――不是因?yàn)镾pring本身的開(kāi)銷(xiāo),
而是因?yàn)榭縎pring容器來(lái)初始化的對(duì)象需要很長(zhǎng)時(shí)間。比如一個(gè)有50-100 Hibernate映射文件的項(xiàng)目可能需要10-20秒來(lái)載入映射文件,
而每次單一測(cè)試fixture的每個(gè)單一測(cè)試前都要這樣的時(shí)間開(kāi)銷(xiāo),減慢了整體的測(cè)試進(jìn)度進(jìn)而降低效率。
測(cè)試類(lèi)通常會(huì)提供一個(gè)數(shù)組來(lái)包含XML配置元數(shù)據(jù)的資源路徑――通常是classpath――來(lái)配置應(yīng)用。這通常和web.xml
或其它部署描述中指定的配置路徑是相同或相近的。
默認(rèn)情況下,一旦載入,ApplicationContext
將在每次測(cè)試中重用。
這樣啟動(dòng)的開(kāi)銷(xiāo)將只需要一次(每個(gè)測(cè)試fixture),接下來(lái)的測(cè)試執(zhí)行就會(huì)快得多。
在一些少見(jiàn)的會(huì)“污染”應(yīng)用上下文的案例中需要重新載入―― 例如,改變一個(gè)bean定義或應(yīng)用對(duì)象的狀態(tài)――
Spring的測(cè)試支持提供了在執(zhí)行下一個(gè)測(cè)試前讓測(cè)試fixture重新載入配置并重建應(yīng)用上下文的機(jī)制。
上下文管理和緩存使用:
JUnit 3.8遺留支持
TestContext框架
當(dāng)Spring集成測(cè)試支持框架載入你的應(yīng)用上下文時(shí),它們能通過(guò)依賴(lài)注入選擇性配置測(cè)試類(lèi)實(shí)例。
這提供了一個(gè)方便的機(jī)制來(lái)使用預(yù)先在應(yīng)用上下文中配置的bean來(lái)搭建測(cè)試fixture。
很大的好處就是你可以在各種測(cè)試場(chǎng)景中重用應(yīng)用上下文(例如配置Spring管理的對(duì)象圖,
事務(wù)代理DataSource
等),從而能避免為單個(gè)的測(cè)試案例重復(fù)進(jìn)行測(cè)試fixture搭建。
作為例子,考慮一個(gè)場(chǎng)景:我們有一個(gè)HibernateTitleDao
類(lèi)來(lái)實(shí)現(xiàn)數(shù)據(jù)訪問(wèn)邏輯,假設(shè)是Title
域?qū)ο蟆N覀兿M帉?xiě)測(cè)試所有以下方面的集成測(cè)試:
Spring配置: 最基本的,是否所有與HibernateTitleDao
bean相關(guān)的配置都是正確和存在的?
Hibernate映射配置文件: 是否所有映射都是正確的并且lazy-loading設(shè)置也到位了?
HibernateTitleDao
邏輯:是否類(lèi)的已配置示例的實(shí)現(xiàn)與預(yù)期相同?
測(cè)試fixtures依賴(lài)注入使用:
JUnit 3.8 遺留支持
TestContext框架
訪問(wèn)實(shí)際數(shù)據(jù)庫(kù)的測(cè)試的一個(gè)通常問(wèn)題是對(duì)持久化狀態(tài)的影響。 即使你使用開(kāi)發(fā)數(shù)據(jù)庫(kù),狀態(tài)的改變也可能影響后面的測(cè)試。而且很多操作 ―― 如插入或修改持久化數(shù)據(jù) ―― 不能在事務(wù)外完成(或驗(yàn)證)。
Spring集成測(cè)試支持框架滿(mǎn)足了這些需求。默認(rèn)情況下,對(duì)每次測(cè)試它們會(huì)創(chuàng)建并回滾事務(wù)。
你編寫(xiě)代碼可以假定事務(wù)已經(jīng)存在。如果你在測(cè)試中調(diào)用事務(wù)代理對(duì)象,它們將根據(jù)配置的事務(wù)語(yǔ)義正常響應(yīng)。
另外,如果測(cè)試方法在事務(wù)內(nèi)刪除了選定表的數(shù)據(jù),這個(gè)事務(wù)會(huì)默認(rèn)回滾,數(shù)據(jù)庫(kù)也將回到測(cè)試執(zhí)行前的狀態(tài)。
事務(wù)支持通過(guò)在測(cè)試應(yīng)用上下文中定義的PlatformTransactionManager
bean提供。
如果你希望事務(wù)被提交 ―― 不常見(jiàn),但可能你希望特定的測(cè)試插入或修改數(shù)據(jù)庫(kù) ―― Spring集成測(cè)試支持框架 可以通過(guò)調(diào)用一個(gè)繼承下來(lái)的鉤子(Hook)方法或聲明特定注解來(lái)讓事務(wù)提交而不是回滾。
事務(wù)管理使用:
JUnit 3.8 遺留支持
TestContext框架
Spring集成測(cè)試支持框架提供了幾個(gè)abstract
支持類(lèi)來(lái)簡(jiǎn)化編寫(xiě)集成測(cè)試。
這些測(cè)試基類(lèi)提供了定義良好的測(cè)試框架鉤子,比如方便的變量實(shí)例和方法,來(lái)訪問(wèn)以下對(duì)象:
ApplicationContext
: 用來(lái)進(jìn)行顯式bean查找或整體測(cè)試上下文狀態(tài)。
JdbcTemplate
或SimpleJdbcTemplate
: 用來(lái)查詢(xún)并確認(rèn)狀態(tài)。
例如,你可能需要在創(chuàng)建對(duì)象并通過(guò)ORM工具持久化到數(shù)據(jù)庫(kù)中的測(cè)試案例運(yùn)行前后進(jìn)行查詢(xún),以確認(rèn)數(shù)據(jù)在數(shù)據(jù)庫(kù)中存在了。
(Spring將確保查詢(xún)?cè)谕粋€(gè)事務(wù)范圍內(nèi)運(yùn)行。) 你需要通知ORM工具來(lái)'flush'變化以確保正常工作,
例如使用Hibernate Session
接口的flush()
方法。
你經(jīng)常會(huì)提供一個(gè)應(yīng)用范圍的超類(lèi)來(lái)為多個(gè)集成測(cè)試提供有用的實(shí)例變量。
支持類(lèi):
JUnit 3.8遺留支持
TestContext框架
org.springframework.test.jdbc
包含有SimpleJdbcTestUtils
類(lèi),它
是一個(gè)基于Java5的JDBC相關(guān)工具方法集,用來(lái)簡(jiǎn)化標(biāo)準(zhǔn)數(shù)據(jù)庫(kù)測(cè)試場(chǎng)景。注意AbstractTransactionalJUnit38SpringContextTests
,
AbstractTransactionalJUnit4SpringContextTests
,
和AbstractTransactionalTestNGSpringContextTests
提供了簡(jiǎn)便的方法來(lái)內(nèi)部代理到SimpleJdbcTestUtils
。
Spring框架在org.springframework.test.annotation
包中提供了常用的Spring特定的注解集,如果你在Java5或以上版本開(kāi)發(fā),可以在測(cè)試中使用它。
@IfProfileValue
提示一下,注解測(cè)試只針對(duì)特定的測(cè)試環(huán)境。
如果配置的ProfileValueSource
類(lèi)返回對(duì)應(yīng)的提供者的名稱(chēng)
值
,
這個(gè)測(cè)試就可以啟動(dòng)。這個(gè)注解可以應(yīng)用到一個(gè)類(lèi)或者單獨(dú)的方法。
@IfProfileValue(name="java.vendor", value="Sun Microsystems Inc.")
public void testProcessWhichRunsOnlyOnSunJvm() {
// some logic that should run only on Java VMs from Sun Microsystems
}
同時(shí)@IfProfileValue
可配置一個(gè)值
列表
(使用OR 語(yǔ)義) 來(lái)在JUnit環(huán)境中獲得TestNG的測(cè)試組支持。
看下面的例子:
@IfProfileValue(name="test-groups", values={"unit-tests", "integration-tests"})
public void testProcessWhichRunsForUnitOrIntegrationTestGroups() {
// some logic that should run only for unit and integration test groups
}
@ProfileValueSourceConfiguration
類(lèi)級(jí)別注解用來(lái)指定當(dāng)通過(guò)@IfProfileValue
注解獲取已配置的profile值時(shí)使用何種ProfileValueSource
。
如果@ProfileValueSourceConfiguration
沒(méi)有在測(cè)試中聲明,將默認(rèn)使用SystemProfileValueSource
。
@ProfileValueSourceConfiguration(CustomProfileValueSource.class)
public class CustomProfileValueSourceTests {
// class body...
}
@DirtiesContext
在測(cè)試方法上出現(xiàn)這個(gè)注解時(shí),表明底層Spring容器在該方法的執(zhí)行中被“污染”,從而必須在方法執(zhí)行結(jié)束后重新創(chuàng)建(無(wú)論該測(cè)試是否通過(guò))。
@DirtiesContext
public void testProcessWhichDirtiesAppCtx() {
// some logic that results in the Spring container being dirtied
}
@ExpectedException
表明被注解方法預(yù)期在執(zhí)行中拋出一個(gè)異常。預(yù)期異常的類(lèi)型在注解中給定。如果該異常的實(shí)例在測(cè)試方法執(zhí)行中被拋出, 則測(cè)試通過(guò)。同樣的如果該異常實(shí)例沒(méi)有在測(cè)試方法執(zhí)行時(shí)拋出,則測(cè)試失敗。
@ExpectedException(SomeBusinessException.class)
public void testProcessRainyDayScenario() {
// some logic that should result in an Exception
being thrown
}
@Timed
表明被注解的測(cè)試方法必須在規(guī)定的時(shí)間區(qū)間內(nèi)執(zhí)行完成(以毫秒記)。如果測(cè)試執(zhí)行時(shí)間超過(guò)了規(guī)定的時(shí)間區(qū)間,測(cè)試就失敗了。
注意該時(shí)間區(qū)間包括測(cè)試方法本身的執(zhí)行,任何重復(fù)測(cè)試(參見(jiàn) @Repeat
),還有任何測(cè)試fixture的set up或tear down時(shí)間。
@Timed(millis=1000)
public void testProcessWithOneSecondTimeout() {
// some logic that should not take longer than 1 second to execute
}
@Repeat
表明被注解的測(cè)試方法必須重復(fù)執(zhí)行。執(zhí)行的次數(shù)在注解中聲明。
注意重復(fù)執(zhí)行范圍包括包括測(cè)試方法本身的執(zhí)行,以及任何測(cè)試fixture的set up或tear down。
@Repeat(10)
public void testProcessRepeatedly() {
// ...
}
@Rollback
表明被注解方法的事務(wù)在完成后是否需要被回滾。
如果true
,事務(wù)將被回滾,否則事務(wù)將被提交。
使用@Rollback
接口來(lái)在類(lèi)級(jí)別覆寫(xiě)配置的默認(rèn)回滾標(biāo)志。
@Rollback(false)
public void testProcessWithoutRollback() {
// ...
}
@NotTransactional
出現(xiàn)該注解表明測(cè)試方法必須不在事務(wù)中執(zhí)行。
@NotTransactional
public void testProcessWithoutTransaction() {
// ...
}
注解支持:
JUnit 3.8遺留支持:
所有上面列舉的注解都被支持,但必須與AbstractAnnotationAwareTransactionalTests
類(lèi)聯(lián)合使用,以保證這些注解能起作用。
TestContext框架:
支持上面列舉的所有注解,而且提供了額外的TestContext特定注解
(例如@ContextConfiguration
、@BeforeTransaction
等等)。
注意,但是一些注解只有與JUnit聯(lián)合使用時(shí)(例如,基于SpringJUnit4ClassRunner
或JUnit 3.8以及JUnit 4.4的測(cè)試類(lèi))。
詳細(xì)內(nèi)容參見(jiàn)TestContext框架章節(jié)。
Spring JUnit 3.8 遺留支持類(lèi)打包在org.springframework.test
包中。
這個(gè)包提供了有用的JUnit TestCase
超類(lèi),
擴(kuò)展它可以在容器外集成測(cè)試中引入Spring ApplicationContext
類(lèi)或在測(cè)試方法級(jí)別獲得事務(wù)支持。
AbstractSingleSpringContextTests
為基于JUnit 3.8的測(cè)試案例提供了上下文管理和緩存支持。
它暴露了一個(gè)protected
方法來(lái)給子類(lèi)覆寫(xiě)以提供上下文定義文件的路徑:
protected String[] getConfigLocations()
這個(gè)方法的實(shí)現(xiàn)必須提供包含XML配置元數(shù)據(jù)的資源路徑 ―― 通常是類(lèi)路徑 ―― 的一個(gè)數(shù)組。
這和在web.xml
或其它部署配置中的資源路徑是相同的或基本相同的。
作為可選方案,你也可以覆寫(xiě)下面的方法。詳細(xì)內(nèi)容參見(jiàn)相關(guān)JavaDoc。
protected String[] getConfigPaths()
protected String getConfigPath()
默認(rèn)情況下,一旦配置文件被載入就會(huì)在每個(gè)測(cè)試案例中重用。
這樣構(gòu)建的開(kāi)銷(xiāo)只會(huì)產(chǎn)生一次(每個(gè)測(cè)試fixture),然后后面的測(cè)試執(zhí)行會(huì)快速的多。
在較少的情況下測(cè)試可能“污染”應(yīng)用上下文,需要重新載入 ―― 例如,
改變一個(gè)bean定義或應(yīng)用對(duì)象狀態(tài) ―― 你可以調(diào)用AbstractSingleSpringContextTests
類(lèi)中的
setDirty()
方法來(lái)讓測(cè)試fixture在執(zhí)行下一個(gè)測(cè)試案例時(shí)重新載
AbstractAnnotationAwareTransactionalTests
類(lèi),
你可以使用@DirtiesContext
來(lái)對(duì)測(cè)試方法進(jìn)行注解以達(dá)到同樣的效果。
當(dāng)AbstractDependencyInjectionSpringContextTests
類(lèi)(及其子類(lèi))載入你的應(yīng)用上下文時(shí),
它們可以通過(guò)Setter注入選擇性配置你的測(cè)試類(lèi)實(shí)例。你需要做的僅僅是定義實(shí)例變量和相應(yīng)的setter方法。
AbstractDependencyInjectionSpringContextTests
將在getConfigLocations()
方法定義的配置文件集中自動(dòng)查找相應(yīng)對(duì)象。
假定這樣一個(gè)場(chǎng)景,我們有一個(gè)HibernateTitleDao
類(lèi)(在通常目標(biāo)章節(jié)詳述)。
讓我們看基于JUnit 3.8 的測(cè)試類(lèi)實(shí)現(xiàn)本身(我們很快將看看配置本身)。
public final class HibernateTitleDaoTests extends AbstractDependencyInjectionSpringContextTests { // this instance will be (automatically) dependency injected private HibernateTitleDao titleDao; // a setter method to enable DI of the 'titleDao' instance variable public void setTitleDao(HibernateTitleDao titleDao) { this.titleDao = titleDao; } public void testLoadTitle() throws Exception { Title title = this.titleDao.loadTitle(new Long(10)); assertNotNull(title); } // specifies the Spring configuration to load for this test fixture protected String[] getConfigLocations() { return new String[] { "classpath:com/foo/daos.xml" }; } }
這個(gè)文件被getConfigLocations()
方法指定(比如,"classpath:com/foo/daos.xml"
) 像這樣:
<?xml version="1.0" encoding="UTF-8"?>
<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-2.5.xsd">
<!-- this bean will be injected into the HibernateTitleDaoTests
class -->
<bean id="titleDao" class="com.foo.dao.hibernate.HibernateTitleDao">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<!-- dependencies elided for clarity -->
</bean>
</beans>
AbstractDependencyInjectionSpringContextTests
類(lèi)使用按類(lèi)型自動(dòng)裝配。
因此如果你有多個(gè)bean定義是相同的類(lèi)型,就不能在這些bean中使用這種方法。
這種情況下,你可以使用繼承的applicationContext
實(shí)例變量并實(shí)現(xiàn)顯式的查找(比如),
調(diào)用applicationContext.getBean("titleDao")
方法。
如果你不希望在測(cè)試案例中使用依賴(lài)注入,只要不聲明任何public
setter方法就可以簡(jiǎn)單實(shí)現(xiàn)。
作為替代的,你可以擴(kuò)展AbstractSpringContextTests
- 在org.springframework.test
包中的JUnit 3.8集成測(cè)試支持類(lèi)層次的根 - 它僅僅包含了一些載入Spring上下文的簡(jiǎn)單方法,而且不在測(cè)試fixture中使用依賴(lài)注入。
如果不管何種原因,你的測(cè)試fixture中沒(méi)有setter方法,Spring可以對(duì)protected
字段進(jìn)行依賴(lài)注入。
下面是前面使用字段級(jí)注入示例的新版本(Spring XML文件無(wú)需改變,僅僅需要改變測(cè)試fixture)。
public final class HibernateTitleDaoTests extends AbstractDependencyInjectionSpringContextTests { public HibernateTitleDaoTests() { // switch on field level injection setPopulateProtectedVariables(true); } // this instance will be (automatically) dependency injected protected HibernateTitleDao titleDao; public void testLoadTitle() throws Exception { Title title = this.titleDao.loadTitle(new Long(10)); assertNotNull(title); } // specifies the Spring configuration to load for this test fixture protected String[] getConfigLocations() { return new String[] { "classpath:com/foo/daos.xml" }; } }
在字段注入的情況下,不能使用自動(dòng)裝配:protected
實(shí)例變量被作為已配置的Spring容器的bean 查找名。
AbstractTransactionalSpringContextTests
類(lèi)
依賴(lài)于應(yīng)用上下文中定義的PlatformTransactionManager
bean。
名字是無(wú)關(guān)緊要的,因?yàn)槭褂昧?span id="wjcelcm34c" class="emphasis">按類(lèi)型自動(dòng)裝配.
通常你會(huì)擴(kuò)展其子類(lèi)AbstractTransactionalDataSourceSpringContextTests
。
這個(gè)類(lèi)也需要在應(yīng)用上下文中有一個(gè)DataSource
bean定義(同樣可以是任意名稱(chēng))。
它創(chuàng)建一個(gè)JdbcTemplate
實(shí)例變量,可以用來(lái)方便的查詢(xún)和刪除選定表的內(nèi)容(
請(qǐng)記住默認(rèn)情況下事務(wù)將被回滾,因而這樣做是安全的)。
如果你希望編程式提交事務(wù) ―― 不常見(jiàn)但對(duì)于特殊的插入數(shù)據(jù)庫(kù)的測(cè)試很有用 ――
你可以調(diào)用繼承自AbstractTransactionalSpringContextTests
類(lèi)的setComplete()
方法。
這將使事務(wù)提交而不是回滾。作為可替代的,如果你在Java 5或更高環(huán)境中開(kāi)發(fā)擴(kuò)展AbstractAnnotationAwareTransactionalTests
類(lèi),
你可以使用@Rollback(false)
來(lái)注解測(cè)試方法,以通過(guò)配置獲得相同的效果。
通過(guò)調(diào)用endTransaction()
方法,這里可以在測(cè)試案例完成時(shí)中止一個(gè)事務(wù)。
默認(rèn)將回滾事務(wù),除非前面調(diào)用了setComplete()
方法。
這個(gè)特性當(dāng)你希望測(cè)試‘?dāng)噙B接’的 數(shù)據(jù)對(duì)象行為是很有用,比如事務(wù)外的web或遠(yuǎn)程使用的Hibernate映射實(shí)體。
通常懶加載錯(cuò)誤只有通過(guò)UI測(cè)試發(fā)現(xiàn)。如果你調(diào)用endTransaction()
方法
可以保證JUnit測(cè)試時(shí)UI操作的正確性。
當(dāng)你擴(kuò)展
AbstractTransactionalDataSourceSpringContextTests
類(lèi)時(shí),你將需要訪問(wèn)下面protected
實(shí)例變量:
applicationContext
(ConfigurableApplicationContext
):
繼承自AbstractSingleSpringContextTests
類(lèi)。使用它可以進(jìn)行顯式bean查找或測(cè)試整個(gè)的上下文狀態(tài)。
jdbcTemplate
: 繼承自AbstractTransactionalDataSourceSpringContextTests
類(lèi),用于查詢(xún)已確認(rèn)狀態(tài)。
例如,應(yīng)用代碼要?jiǎng)?chuàng)建一個(gè)對(duì)象,然后使用ORM工具將其持久化,這時(shí)你想在測(cè)試代碼執(zhí)行前后對(duì)其進(jìn)行查詢(xún),以確定數(shù)據(jù)是否插入到數(shù)據(jù)庫(kù)中(Spring會(huì)保證該查詢(xún)運(yùn)行在相同事務(wù)內(nèi))。你需要告訴你的ORM工具‘清空’其改變以正確完成任務(wù),例如,使用HibernateSession
接口的flush()
方法。
在上述常用注解之外,
org.springframework.test.annotation
包也有一個(gè)抽象
JUnit
TestCase
類(lèi)來(lái)提供注解驅(qū)動(dòng)的集成測(cè)試支持。
AbstractAnnotationAwareTransactionalTests
類(lèi)擴(kuò)展了AbstractTransactionalDataSourceSpringContextTests
類(lèi),
并通過(guò)擴(kuò)展fixture引入一些(Spring專(zhuān)有)的注解。AbstractAnnotationAwareTransactionalTests
支持所有常用注解章節(jié)中列舉的注解,
而且包括Spring的@Transactional
注解,以顯式配置事務(wù)語(yǔ)義。
org.springframework.test.jpa
包提供了基于Java 持久化API(JPA)的測(cè)試支持類(lèi)。
AbstractJpaTests
是一個(gè)方便的JPA相關(guān)測(cè)試的支持類(lèi),
它提供了和AbstractTransactionalDataSourceSpringContextTests
相同的功能和即使在進(jìn)行JPA規(guī)范需要的性能測(cè)試時(shí)也相同的性能。
它暴露了一個(gè)EntityManagerFactory
接口和一個(gè)共享的EntityManager
接口。
需要注入一個(gè)EntityManagerFactory
接口,
以及通過(guò)超類(lèi)獲得DataSource
接口和JpaTransactionManager
接口。
AbstractAspectjJpaTests
類(lèi)是AbstractJpaTests
的子類(lèi),
它激活了AspectJ 的裝載期織入并能夠讓AspectJ指定一個(gè)自定義的aop.xml
文件路徑。
Spring TestContext
Framework (在org.springframework.test.context
包中)
提供了一般的、注解驅(qū)動(dòng)的單元和集成測(cè)試支持,它對(duì)使用的測(cè)試框架不做要求,可以使用JUnit 3.8、JUnit 4.4, TestNG 5.5等等。
TestContext框架也強(qiáng)調(diào)了約定優(yōu)于配置的重要性,它提供了合理的默認(rèn)值,同時(shí)也可以通過(guò)基于注解的配置進(jìn)行改寫(xiě)。
除了一般的測(cè)試基礎(chǔ)設(shè)施外,TestContext框架還以抽象
支持類(lèi)的形式對(duì)JUnit 3.8、JUnit 4.4和TestNG 5.5提供了顯式的支持。
針對(duì)JUnit 4.4,該框架還提供了一個(gè)自定義的Runner
,這使得用戶(hù)無(wú)需繼承特定的類(lèi)就可以編寫(xiě)測(cè)試類(lèi)了。
以下章節(jié)給出了TestContext框架的內(nèi)部概覽。 如果你僅僅關(guān)注如何使用該框架而不是使用你自己的監(jiān)聽(tīng)器去擴(kuò)展它,那么請(qǐng)直接跳到配置(上下文管理和緩存、 依賴(lài)注入、事務(wù)管理)、 支持類(lèi)及注解支持章節(jié)。
框架的核心包括TestContext
和TestContextManager
類(lèi)以及TestExecutionListener
接口。
每次測(cè)試都會(huì)創(chuàng)建TestContextManager
。TestContextManager
管理了一個(gè)TestContext
,
它負(fù)責(zé)持有當(dāng)前測(cè)試的上下文。TestContextManager
還負(fù)責(zé)在測(cè)試執(zhí)行過(guò)程中更新TestContext
的狀態(tài)并代理到TestExecutionListener
,
它用來(lái)監(jiān)測(cè)測(cè)試實(shí)際的執(zhí)行(如提供依賴(lài)注入、管理事務(wù)等等)。請(qǐng)查看JavaDoc及Spring測(cè)試套件以獲得進(jìn)一步的信息和各種配置示例。
TestContext
:封裝測(cè)試執(zhí)行的上下文,與當(dāng)前使用的測(cè)試框架無(wú)關(guān)。
TestContextManager
:Spring TestContext Framework的主入口點(diǎn),
負(fù)責(zé)管理單獨(dú)的TestContext
并在定義好的執(zhí)行點(diǎn)上向所有注冊(cè)的TestExecutionListener
發(fā)出事件通知:
測(cè)試實(shí)例的準(zhǔn)備,先于特定的測(cè)試框架的前置方法,遲于后置方法。
TestExecutionListener
:定義了一個(gè)監(jiān)聽(tīng)器API與TestContextManager
發(fā)布的測(cè)試執(zhí)行事件進(jìn)行交互,
而該監(jiān)聽(tīng)器就是注冊(cè)到這個(gè)TestContextManager
上的。
Spring提供了TestExecutionListener
的三個(gè)實(shí)現(xiàn),
他們都是使用默認(rèn)值進(jìn)行配置的(通過(guò)@TestExecutionListeners
注解):
DependencyInjectionTestExecutionListener
、DirtiesContextTestExecutionListener
及TransactionalTestExecutionListener
,
他們對(duì)測(cè)試實(shí)例提供了依賴(lài)注入支持,處理@DirtiesContext
注解,并分別使用默認(rèn)的回滾語(yǔ)義對(duì)測(cè)試提供事務(wù)支持。
以下三個(gè)章節(jié)講述了如何通過(guò)注解配置TestContext
框架并提供了使用該框架編寫(xiě)真實(shí)的單元和集成測(cè)試的示例。
每個(gè)TestContext
都會(huì)為其所負(fù)責(zé)的測(cè)試實(shí)例提供上下文和緩存管理。
測(cè)試實(shí)例不會(huì)自動(dòng)訪問(wèn)配置好的ApplicationContext
;然而,如果一個(gè)測(cè)試類(lèi)實(shí)現(xiàn)了ApplicationContextAware
接口,
那么測(cè)試實(shí)例就會(huì)擁有一個(gè)對(duì)ApplicationContext
的引用(假如默認(rèn)已經(jīng)配置好了DependencyInjectionTestExecutionListener
)。
AbstractJUnit38SpringContextTests
、
AbstractJUnit4SpringContextTests
及AbstractTestNGSpringContextTests
已經(jīng)實(shí)現(xiàn)了ApplicationContextAware
,
因此自帶了上述功能。
與JUnit 3.8遺留支持不同,使用TestContext框架的測(cè)試類(lèi)無(wú)需重寫(xiě)任何protected
成員方法來(lái)配置應(yīng)用上下文。
只需在類(lèi)層次上聲明@ContextConfiguration
注解就可以完成配置。
如果你的測(cè)試類(lèi)沒(méi)有顯式聲明任何應(yīng)用上下文資源的位置
,那么配置好的ContextLoader
就會(huì)決定如何以及是否從默認(rèn)的集合位置上加載一個(gè)上下文。
例如,GenericXmlContextLoader
- 默認(rèn)的ContextLoader
- 會(huì)基于測(cè)試類(lèi)的名字產(chǎn)生一個(gè)默認(rèn)的位置。
如果類(lèi)名叫做com.example.MyTest
,那么GenericXmlContextLoader
就會(huì)從"classpath:/com/example/MyTest-context.xml"
加載應(yīng)用上下文。
package com.example;
@RunWith(SpringJUnit4ClassRunner.class)
// ApplicationContext will be loaded from "classpath:/com/example/MyTest-context.xml"
@ContextConfiguration
public class MyTest {
// class body...
}
如果默認(rèn)位置不適合你的需求,你可以使用一個(gè)包含了XML配置元數(shù)據(jù)的資源位置的數(shù)組來(lái)配置@ContextConfiguration
的locations
屬性
(假如已經(jīng)配置好了一個(gè)可以使用XML的ContextLoader
)- 一般在classpath上,該屬性被用來(lái)配置應(yīng)用程序。
這就和在web.xml
或者其他部署配置中指定配置列表時(shí),方法完全一樣,或者幾乎一樣。
作為另外一種選擇,你可以實(shí)現(xiàn)并配置自己的ContextLoader
@RunWith(SpringJUnit4ClassRunner.class) // ApplicationContext will be loaded from"/applicationContext.xml"
and"/applicationContext-test.xml"
// in the root of the classpath @ContextConfiguration(locations={"/applicationContext.xml", "/applicationContext-test.xml"}) public class MyTest { // class body... }
@ContextConfiguration
還提供了一個(gè)boolean類(lèi)型的inheritLocations
屬性以表明是否繼承父類(lèi)的locations。
其默認(rèn)值是true
,表明一個(gè)被注解的類(lèi)會(huì)繼承被注解的父類(lèi)中定義的資源位置。
特別地,一個(gè)被注解的類(lèi)的資源位置會(huì)附加到其被注解的父類(lèi)中的資源位置列表上。這樣子類(lèi)就可以繼承資源位置列表。
在下面的例子中,將按順序從"/base-context.xml"和"/extended-context.xml"中加載針對(duì)ExtendedTest
的ApplicationContext
。
所以定義在"/extended-context.xml"中的Beans會(huì)覆蓋掉定義在"/base-context.xml"中的Beans。
@RunWith(SpringJUnit4ClassRunner.class) // ApplicationContext will be loaded from"/base-context.xml"
in the root of the classpath @ContextConfiguration(locations={"/base-context.xml"}) public class BaseTest { // class body... } // ApplicationContext will be loaded from"/base-context.xml"
and"/extended-context.xml"
// in the root of the classpath @ContextConfiguration(locations={"/extended-context.xml"}) public class ExtendedTest extends BaseTest { // class body... }
如果將inheritLocations
設(shè)為false
,那么就會(huì)屏蔽掉父類(lèi)的資源位置,然后可以替換父類(lèi)中定義的任何資源位置。
默認(rèn)情況下, 配置好的ApplicationContext
一旦被加載就會(huì)重用到每個(gè)測(cè)試上。這樣設(shè)置的成本僅產(chǎn)生一次(每個(gè)測(cè)試fixture),
隨后測(cè)試的執(zhí)行就會(huì)很快了。在某些不太可能發(fā)生的情況下,一個(gè)測(cè)試可能會(huì)破壞應(yīng)用上下文,
這時(shí)它需要重新加載 - 例如,通過(guò)改變應(yīng)用對(duì)象的bean定義或者狀態(tài) - 你可以使用@DirtiesContext
(假設(shè)默認(rèn)已經(jīng)配置了DirtiesContextTestExecutionListener
)來(lái)注解測(cè)試方法使得測(cè)試fixture重新加載配置文件并在測(cè)試下次執(zhí)行前重新構(gòu)建應(yīng)用上下文。
當(dāng)你配置DependencyInjectionTestExecutionListener
時(shí) - 它會(huì)被默認(rèn)配置 - 通過(guò)@TestExecutionListeners
注解,
你的測(cè)試實(shí)例依賴(lài)的bean會(huì)被注入,而這些bean是通過(guò)@ContextConfiguration
使用Setter注入、Field注入或者兩者都有來(lái)注入的,
到底使用哪種方式取決于你選擇的注解以及你將它們放到setter方法中還是屬性中。為了與Spring 2.5的注解保持一致,
你可以選擇Spring的@Autowired
注解或者JSR 250中的@Resource
注解。其語(yǔ)義對(duì)于Spring框架來(lái)說(shuō)都是一致的。
例如, 如果你喜歡 按類(lèi)型自動(dòng)織入,
那么請(qǐng)使用@Autowired
來(lái)注解你的settter方法或者屬性。另一方面,如果你喜歡按名字注入,
那么請(qǐng)使用@Resource
來(lái)注解你的settter方法或者屬性。
TestContext框架沒(méi)有監(jiān)測(cè)測(cè)試實(shí)例的實(shí)例化方式。所以對(duì)構(gòu)造方法使用@Autowired
將毫無(wú)意義。
既然@Autowired
執(zhí)行按類(lèi)型自動(dòng)編織,
那么如果你有相同類(lèi)型的多個(gè)bean定義的話(huà),對(duì)那些特定的bean就不能使用該方式。在這種情況下,
你可以使用@Resource
按名字注入。另外,如果你的測(cè)試類(lèi)實(shí)現(xiàn)了ApplicationContextAware
,
就可以直接訪問(wèn)ApplicationContext
并調(diào)用applicationContext.getBean("titleDao")
執(zhí)行一個(gè)顯式的查找。
如果你不想讓你的測(cè)試實(shí)例使用依賴(lài)注入,只要不將@Autowired
或者@Resource
注解到任何屬性或者setter方法上就行了。
另一種方式,你可以使用@TestExecutionListeners
并忽略掉監(jiān)聽(tīng)器列表中的DependencyInjectionTestExecutionListener.class
就可以完全禁用依賴(lài)注入。
考慮如下場(chǎng)景:我們有一個(gè)類(lèi),名字叫HibernateTitleDao
(通用目標(biāo)章節(jié)已經(jīng)進(jìn)行了介紹)。
首先,讓我們看看基于JUnit 4.4的測(cè)試類(lèi)的實(shí)現(xiàn),它使用@Autowired
進(jìn)行屬性注入(在所有示例代碼之后我們會(huì)查看應(yīng)用上下文的配置)。
注意:下面代碼的依賴(lài)注入行為并不是特定于JUnit 4.4的。同樣的依賴(lài)注入技術(shù)可以使用在任何測(cè)試框架中。
@RunWith(SpringJUnit4ClassRunner.class) // specifies the Spring configuration to load for this test fixture @ContextConfiguration(locations={"daos.xml"}) public final class HibernateTitleDaoTests { // this instance will be dependency injected by type @Autowired private HibernateTitleDao titleDao; public void testLoadTitle() throws Exception { Title title = this.titleDao.loadTitle(new Long(10)); assertNotNull(title); } }
此外,我們可以使用@Autowired
進(jìn)行setter注入。
@RunWith(SpringJUnit4ClassRunner.class) // specifies the Spring configuration to load for this test fixture @ContextConfiguration(locations={"daos.xml"}) public final class HibernateTitleDaoTests { // this instance will be dependency injected by type private HibernateTitleDao titleDao; @Autowired public void setTitleDao(HibernateTitleDao titleDao) { this.titleDao = titleDao; } public void testLoadTitle() throws Exception { Title title = this.titleDao.loadTitle(new Long(10)); assertNotNull(title); } }
現(xiàn)在讓我們看看使用@Resource
進(jìn)行屬性注入的一個(gè)示例。
@RunWith(SpringJUnit4ClassRunner.class) // specifies the Spring configuration to load for this test fixture @ContextConfiguration(locations={"daos.xml"}) public final class HibernateTitleDaoTests { // this instance will be dependency injected by name @Resource private HibernateTitleDao titleDao; public void testLoadTitle() throws Exception { Title title = this.titleDao.loadTitle(new Long(10)); assertNotNull(title); } }
最后,這是使用@Resource
進(jìn)行setter注入的一個(gè)示例。
@RunWith(SpringJUnit4ClassRunner.class) // specifies the Spring configuration to load for this test fixture @ContextConfiguration(locations={"daos.xml"}) public final class HibernateTitleDaoTests { // this instance will be dependency injected by name private HibernateTitleDao titleDao; @Resource public void setTitleDao(HibernateTitleDao titleDao) { this.titleDao = titleDao; } public void testLoadTitle() throws Exception { Title title = this.titleDao.loadTitle(new Long(10)); assertNotNull(title); } }
上面的代碼使用了相同的XML上下文文件,@ContextConfiguration
注解使用了這些信息(如 "daos.xml"
),它是這樣的:
<?xml version="1.0" encoding="UTF-8"?>
<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-2.5.xsd">
<!-- this bean will be injected into the HibernateTitleDaoTests
class -->
<bean id="titleDao" class="com.foo.dao.hibernate.HibernateTitleDao">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<!-- dependencies elided for clarity -->
</bean>
</beans>
在TestContext框架中,事務(wù)是由TransactionalTestExecutionListener
進(jìn)行管理的,
默認(rèn)情況下這是通過(guò)@TestExecutionListeners
注解進(jìn)行配置的,
即使你沒(méi)有在測(cè)試類(lèi)中顯式聲明 @TestExecutionListeners
注解。
為了支持事務(wù),你必須通過(guò)@ContextConfiguration
在應(yīng)用上下文中加載一個(gè)PlatformTransactionManager
bean。
此外,你必須在類(lèi)或方法層次上聲明一個(gè)@Transactional
。
請(qǐng)參考TestContext框架的注解支持中的@TransactionConfiguration
以了解類(lèi)層次的事務(wù)配置(例如為事務(wù)管理器設(shè)置bean名稱(chēng)以及默認(rèn)的回滾標(biāo)志)。
為每個(gè)測(cè)試方法配置事務(wù)時(shí)有幾種選項(xiàng)。如果對(duì)于整個(gè)類(lèi)來(lái)說(shuō)事務(wù)不可用,那么可以使用@Transactional
來(lái)顯式注解方法。
與此類(lèi)似,如果對(duì)于整個(gè)類(lèi)來(lái)說(shuō)事務(wù)可用,那么可以使用@NotTransactional
來(lái)注解方法表明不為該方法增加事務(wù)。
你可以使用@Rollback
注解覆蓋類(lèi)級(jí)別的默認(rèn)的回滾設(shè)置進(jìn)而針對(duì)一個(gè)特定的測(cè)試方法控制其事務(wù)的提交。
請(qǐng)注意,AbstractTransactionalJUnit38SpringContextTests
、
AbstractTransactionalJUnit4SpringContextTests
及
AbstractTransactionalTestNGSpringContextTests
已經(jīng)在類(lèi)級(jí)別預(yù)先配置好了事務(wù)支持。
偶爾你需要在一個(gè)事務(wù)性測(cè)試方法前、后執(zhí)行某些代碼,而這些代碼是處在事務(wù)上下文之外的,例如,
在測(cè)試執(zhí)行前去驗(yàn)證初始的數(shù)據(jù)庫(kù)狀態(tài)或者在測(cè)試執(zhí)行后驗(yàn)證期待的事務(wù)提交行為(舉例來(lái)說(shuō),該測(cè)試被配置為不進(jìn)行回滾的)。
支持@BeforeTransaction
和@AfterTransaction
注解的TransactionalTestExecutionListener
正好適用于這種情況。
使用這些注解之一來(lái)注解測(cè)試類(lèi)中任何的public void
方法,
同時(shí)TransactionalTestExecutionListener
會(huì)保證你的事務(wù)方法之前的代碼或者事務(wù)方法之后的代碼會(huì)在正確的時(shí)間執(zhí)行。
任意前置方法 (如使用JUnit 4的@Before所注解的方法)和后置方法 (如使用JUnit 4的@After所注解的方法)都會(huì)在一個(gè)事務(wù)中得到執(zhí)行。
此外,使用 @NotTransactional
注解的測(cè)試不會(huì)執(zhí)行@BeforeTransaction
或@AfterTransaction
所注解的方法。
下面的基于JUnit 4的示例展示了一個(gè)假想的集成測(cè)試場(chǎng)景,重點(diǎn)闡述了事務(wù)相關(guān)的注解。請(qǐng)查看參考手冊(cè)的TestContext框架注解支持章節(jié)以了解進(jìn)一步的信息和配置示例。
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration @TransactionConfiguration(transactionManager="txMgr", defaultRollback=false) @Transactional public class FictitiousTransactionalTest { @BeforeTransaction public void verifyInitialDatabaseState() { // logic to verify the initial state before a transaction is started } @Before public void setUpTestDataWithinTransaction() { // set up test data within the transaction } @Test // overrides the class-level defaultRollback setting @Rollback(true) public void modifyDatabaseWithinTransaction() { // logic which uses the test data and modifies database state } @After public void tearDownWithinTransaction() { // execute "tear down" logic within the transaction } @AfterTransaction public void verifyFinalDatabaseState() { // logic to verify the final state after transaction has rolled back } @Test @NotTransactional public void performNonDatabaseRelatedAction() { // logic which does not modify database state } }
org.springframework.test.context.junit38
包為基于JUnit 3.8的測(cè)試用例提供了支持類(lèi)。
AbstractJUnit38SpringContextTests
:
對(duì)集成了Spring TestContext Framework與JUnit 3.8環(huán)境中的ApplicationContext
測(cè)試支持的TestCase
進(jìn)行了抽象。
當(dāng)你繼承AbstractJUnit38SpringContextTests
類(lèi)時(shí),你就可以訪問(wèn)到protected
的成員變量:
applicationContext
:使用它進(jìn)行顯式的bean查找或者測(cè)試整個(gè)上下文的狀態(tài)。
AbstractTransactionalJUnit38SpringContextTests
:
對(duì)為JDBC訪問(wèn)增加便捷功能的AbstractJUnit38SpringContextTests
的事務(wù)擴(kuò)展進(jìn)行抽象。
需要在ApplicationContext
中定義一個(gè)javax.sql.DataSource
bean和一個(gè)PlatformTransactionManager
bean。
當(dāng)你繼承AbstractTransactionalJUnit38SpringContextTests
類(lèi)時(shí),你就可以訪問(wèn)到protected
的成員變量:
applicationContext
:從AbstractJUnit38SpringContextTests
父類(lèi)繼承。使用它執(zhí)行bean的查找或者測(cè)試整個(gè)上下文的狀態(tài)
simpleJdbcTemplate
:在查詢(xún)以確認(rèn)狀態(tài)時(shí)非常有用。例如,應(yīng)用代碼要?jiǎng)?chuàng)建一個(gè)對(duì)象,然后使用ORM工具將其持久化,
這時(shí)你想在測(cè)試代碼執(zhí)行前后對(duì)其進(jìn)行查詢(xún),以確定數(shù)據(jù)是否插入到數(shù)據(jù)庫(kù)中(Spring會(huì)保證該查詢(xún)運(yùn)行在相同事務(wù)內(nèi))。
你需要告訴你的ORM工具‘flush’其改變以正確完成任務(wù),例如,使用HibernateSession
接口的flush()
方法。
org.springframework.test.context.junit4
包為基于JUnit 4.4的測(cè)試用例提供了支持類(lèi)。
AbstractJUnit4SpringContextTests
:
對(duì)集成了Spring TestContext Framework與JUnit 4.4環(huán)境中的ApplicationContext
測(cè)試支持的基本測(cè)試類(lèi)進(jìn)行了抽取。
當(dāng)你繼承AbstractJUnit4SpringContextTests
時(shí),你就可以訪問(wèn)到protected
的成員變量:
applicationContext
:使用它進(jìn)行顯式的bean查找或者測(cè)試整個(gè)上下文的狀態(tài)。
AbstractTransactionalJUnit4SpringContextTests
:
對(duì)為JDBC訪問(wèn)增加便捷功能的AbstractJUnit4SpringContextTests
的事務(wù)擴(kuò)展進(jìn)行抽象。
需要在ApplicationContext
中定義一個(gè)javax.sql.DataSource
bean和一個(gè)PlatformTransactionManager
bean。
當(dāng)你繼承AbstractTransactionalJUnit4SpringContextTests
類(lèi)時(shí),你就可以訪問(wèn)到下列protected
的成員變量:
applicationContext
:繼承自父類(lèi)AbstractJUnit4SpringContextTests
。
使用它執(zhí)行bean的查找或者測(cè)試整個(gè)上下文的狀態(tài)
simpleJdbcTemplate
:當(dāng)通過(guò)查詢(xún)來(lái)確認(rèn)狀態(tài)時(shí)非常有用。例如,應(yīng)用代碼要?jiǎng)?chuàng)建一個(gè)對(duì)象,
然后使用ORM工具將其持久化,這時(shí)你想在測(cè)試代碼執(zhí)行前后對(duì)其進(jìn)行查詢(xún),以確定數(shù)據(jù)是否插入到數(shù)據(jù)庫(kù)中。
(Spring會(huì)保證該查詢(xún)運(yùn)行在相同事務(wù)內(nèi)。)你需要告訴你的ORM工具‘flush’其改變以正確完成任務(wù),例如,
使用HibernateSession
接口的flush()
方法。
這些類(lèi)僅僅為擴(kuò)展提供了方便。 如果你不想將你的測(cè)試類(lèi)綁定到Spring的類(lèi)上 - 例如,如果你要直接擴(kuò)展你想測(cè)試的類(lèi) - 只需要通過(guò)@RunWith(SpringJUnit4ClassRunner.class)
、
@ContextConfiguration
、@TestExecutionListeners
等注解來(lái)配置你自己的測(cè)試類(lèi)就可以了。
Spring TestContext Framework通過(guò)一個(gè)可定制的運(yùn)行器提供了與JUnit 4.4的完全集成。
通過(guò)使用@Runwith(SpringJUnit4ClassRunner.class)
來(lái)注解測(cè)試類(lèi),開(kāi)發(fā)者可以實(shí)現(xiàn)標(biāo)準(zhǔn)的JUnit 4.4單元和集成測(cè)試,
同時(shí)還能獲得TestContext框架的好處,如對(duì)加載應(yīng)用上下文的支持,測(cè)試實(shí)例的依賴(lài)注入,執(zhí)行事務(wù)性測(cè)試方法等等。
下面的代碼清單顯示了使用定制的Spring Runner來(lái)配置一個(gè)測(cè)試類(lèi)的最小需求。
注意,我們使用一個(gè)空的列表來(lái)配置@TestExecutionListeners
以便禁用默認(rèn)的監(jiān)聽(tīng)器,
否則需要通過(guò)@ContextConfiguration
配置一個(gè) ApplicationContext
。
@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({})
public class SimpleTest {
@Test
public void testMethod() {
// execute test logic...
}
}
org.springframework.test.context.testng
包為基于TestNG的測(cè)試用例提供了支持類(lèi)。
AbstractTestNGSpringContextTests
:
對(duì)集成了Spring TestContext Framework與TestNG環(huán)境中的ApplicationContext
測(cè)試支持的基礎(chǔ)測(cè)試類(lèi)進(jìn)行了抽象。
當(dāng)你繼承AbstractTestNGSpringContextTests
時(shí),就可以訪問(wèn)到下列protected
的成員變量:
applicationContext
:使用它進(jìn)行顯式的bean查找或者測(cè)試整個(gè)上下文的狀態(tài)。
AbstractTransactionalTestNGSpringContextTests
:
對(duì)為JDBC訪問(wèn)增加便捷功能的AbstractTestNGSpringContextTests
的事務(wù)擴(kuò)展進(jìn)行抽象。
需要在ApplicationContext
中定義一個(gè)javax.sql.DataSource
bean和一個(gè)PlatformTransactionManager
bean。
當(dāng)你繼承AbstractTransactionalTestNGSpringContextTests
類(lèi)時(shí),就可以訪問(wèn)下列protected
的成員變量:
applicationContext
:繼承自父類(lèi)AbstractTestNGSpringContextTests
。使用它執(zhí)行bean的查找或者測(cè)試整個(gè)上下文的狀態(tài)。
simpleJdbcTemplate
:當(dāng)通過(guò)查詢(xún)來(lái)確認(rèn)狀態(tài)時(shí)非常有用。例如,應(yīng)用代碼要?jiǎng)?chuàng)建一個(gè)對(duì)象,
然后使用ORM工具將其持久化,這時(shí)你想在測(cè)試代碼執(zhí)行前后對(duì)其進(jìn)行查詢(xún),以確定數(shù)據(jù)是否插入到數(shù)據(jù)庫(kù)中。(Spring會(huì)保證該查詢(xún)運(yùn)行在相同事務(wù)內(nèi)。)
你需要告訴你的ORM工具‘flush’其改變以正確完成任務(wù),例如,使用HibernateSession
接口的flush()
方法。
這些類(lèi)僅僅為擴(kuò)展提供了方便。 如果你不想將你的測(cè)試類(lèi)綁定到Spring的類(lèi)上 - 例如,如果你要直接擴(kuò)展你想測(cè)試的類(lèi) - 只需要通過(guò)
@ContextConfiguration
、@TestExecutionListeners
等注解來(lái)配置你自己的測(cè)試類(lèi)就可以了。
并使用TestContextManager
來(lái)手工監(jiān)測(cè)你的測(cè)試類(lèi)。
請(qǐng)查看AbstractTestNGSpringContextTests
的源代碼以了解如何檢測(cè)你自己的測(cè)試類(lèi)。
Spring TestContext Framework支持通用注解章節(jié)提到的所有注解。 然而下面的這些注解只有配合JUnit才能使用(比如搭配SpringJUnit4ClassRunner或者 JUnit 3.8及JUnit 4.4支持類(lèi))。
@IfProfileValue
@ProfileValueSourceConfiguration
@ExpectedException
協(xié)同使用Spring的@ExpectedException
注解與JUnit 4的@Test(expected=...)
會(huì)導(dǎo)致一個(gè)不可避免的沖突。
因此當(dāng)與JUnit 4集成時(shí),開(kāi)發(fā)者必須選擇其中一個(gè),在這種情況下建議使用顯式的JUnit 4配置。
@Timed
Spring的@Timed
注解與JUnit 4的@Test(timeout=...)
支持具有不同的語(yǔ)義。
特別地,鑒于JUnit 4處理測(cè)試執(zhí)行超時(shí)(如通過(guò)在一個(gè)單獨(dú)的線(xiàn)程
中執(zhí)行測(cè)試方法)的方式,
我們不可能在一個(gè)事務(wù)上下文中的測(cè)試方法上使用JUnit的@Test(timeout=...)
配置。因此,
如果你想將一個(gè)測(cè)試方法配置成計(jì)時(shí)且具事務(wù)性的,
你就必須聯(lián)合使用Spring的@Timed
及@Transactional
注解。
還值得注意的是@Test(timeout=...)
只管測(cè)試方法本身執(zhí)行的次數(shù),如果超出的話(huà)立刻就會(huì)失??;
然而,@Timed
關(guān)注的是測(cè)試執(zhí)行的總時(shí)間(包括建立和銷(xiāo)毀操作以及重復(fù)),并且不會(huì)令測(cè)試失敗。
@Repeat
Spring TestContext Framework還支持下面這些非特定于測(cè)試的注解,并且保持其語(yǔ)義不變。
@Autowired
@Qualifier
@Resource
(javax.annotation)如果JSR-250可用
@PersistenceContext
(javax.persistence)如果JPA可用
@PersistenceUnit
(javax.persistence)如果JPA可用
@Required
@Transactional
下面的列表包含了特定于Spring TestContext Framework的所有注解。請(qǐng)查看相應(yīng)的JavaDoc以了解進(jìn)一步的信息,包括默認(rèn)的屬性值等等。
@ContextConfiguration
定義類(lèi)級(jí)別的元數(shù)據(jù)以決定如何加載和配置ApplicationContext
。特別地,
@ContextConfiguration定義了要加載的應(yīng)用上下文資源位置
以及用來(lái)加載上下文的ContextLoader
策略。
@ContextConfiguration(locations={"example/test-context.xml"}, loader=CustomContextLoader.class)
public class CustomConfiguredApplicationContextTests {
// class body...
}
注意:@ContextConfiguration
默認(rèn)情況下為繼承的資源位置提供了支持。
查看上下文管理和緩存章節(jié)及JavaDoc來(lái)了解更多的示例和細(xì)節(jié)信息。
@TestExecutionListeners
定義類(lèi)級(jí)別的元數(shù)據(jù),TestExecutionListener
s會(huì)使用TestContextManager
進(jìn)行注冊(cè)。
通常,@TestExecutionListeners
與@ContextConfiguration
會(huì)搭配使用。
@ContextConfiguration
@TestExecutionListeners({CustomTestExecutionListener.class, AnotherTestExecutionListener.class})
public class CustomTestExecutionListenerTests {
// class body...
}
注意:@TestExecutionListeners
默認(rèn)情況下為繼承的監(jiān)聽(tīng)器提供了支持。查看JavaDoc來(lái)了解更多的示例和細(xì)節(jié)信息。
@TransactionConfiguration
為配置事務(wù)性測(cè)試定義了類(lèi)級(jí)別的元數(shù)據(jù)。特別地,如果需要的PlatformTransactionManager不是“transactionManager”的話(huà),
那么可以顯式配置驅(qū)動(dòng)事務(wù)的PlatformTransactionManager
的bean名字。此外,
可以將defaultRollback
標(biāo)志改為false
。通常,
@TransactionConfiguration
與@ContextConfiguration
搭配使用。
@ContextConfiguration
@TransactionConfiguration(transactionManager="txMgr", defaultRollback=false)
public class CustomConfiguredTransactionalTests {
// class body...
}
@BeforeTransaction
表明被注解的public void
方法應(yīng)該在測(cè)試方法的事務(wù)開(kāi)始之前執(zhí)行,
該事務(wù)是通過(guò)@Transactional
注解來(lái)配置的。
@BeforeTransaction
public void beforeTransaction() {
// logic to be executed before a transaction is started
}
@AfterTransaction
表明被注解的public void
方法應(yīng)該在測(cè)試方法的事務(wù)結(jié)束之后執(zhí)行,
該事務(wù)是通過(guò)@Transactional
注解來(lái)配置的。
@AfterTransaction
public void afterTransaction() {
// logic to be executed after a transaction has ended
}
在Spring的完整發(fā)行包里包含了PetClinic示例應(yīng)用,它以JUnit 4.4環(huán)境闡述了Spring TestContext Framework的幾個(gè)特性。
大多數(shù)功能包含在AbstractClinicTests
里,部分內(nèi)容列舉如下:
@ContextConfiguration public abstract class AbstractClinicTests extends AbstractTransactionalJUnit4SpringContextTests { @Autowired protected Clinic clinic; @Test public void getVets() { Collection<Vet> vets = this.clinic.getVets(); assertEquals("JDBC query must show the same number of vets", super.countRowsInTable("VETS"), vets.size()); Vet v1 = EntityUtils.getById(vets, Vet.class, 2); assertEquals("Leary", v1.getLastName()); assertEquals(1, v1.getNrOfSpecialties()); assertEquals("radiology", (v1.getSpecialties().get(0)).getName()); // ... } // ... }
注意:
該測(cè)試用例繼承了AbstractTransactionalJUnit4SpringContextTests
類(lèi),
從這里它繼承了針對(duì)依賴(lài)注入的配置(通過(guò)DependencyInjectionTestExecutionListener
)和事務(wù)性行為(通過(guò)TransactionalTestExecutionListener
)。
clinic
成員變量 - 要測(cè)試的應(yīng)用程序?qū)ο?- 是通過(guò)@Autowired
進(jìn)行依賴(lài)注入的。
testGetVets()
方法說(shuō)明如何使用繼承下來(lái)的countRowsInTable()
方法來(lái)輕松驗(yàn)證表中的行數(shù),
進(jìn)而測(cè)試應(yīng)用代碼的正確行為。這點(diǎn)允許實(shí)現(xiàn)更強(qiáng)大的測(cè)試,減少了對(duì)確切測(cè)試數(shù)據(jù)的依賴(lài)。例如,無(wú)需打斷測(cè)試就可以向數(shù)據(jù)庫(kù)中增加新行。
像很多使用數(shù)據(jù)庫(kù)的集成測(cè)試一樣,AbstractClinicTests
中的大多數(shù)測(cè)試依賴(lài)于測(cè)試運(yùn)行前數(shù)據(jù)庫(kù)中已有的最小量的數(shù)據(jù)。
但是你可能在測(cè)試用例中改變數(shù)據(jù)庫(kù)――當(dāng)然,在同一個(gè)事務(wù)中。
PetClinic應(yīng)用支持三種數(shù)據(jù)訪問(wèn)技術(shù) - JDBC、Hibernate及JPA。無(wú)需任何特定的資源位置,
只要聲明了@ContextConfiguration
,那么AbstractClinicTests
類(lèi)就會(huì)從默認(rèn)位置加載其應(yīng)用上下文,
該默認(rèn)位置為"AbstractClinicTests-context.xml"
,這里聲明了一個(gè)通用的DataSource
。
子類(lèi)指定了額外的上下文位置,這就要求它必須聲明一個(gè)PlatformTransactionManager
和Clinic
的一個(gè)具體實(shí)現(xiàn)。
例如,PetClinic測(cè)試的Hibernate實(shí)現(xiàn)包含以下實(shí)現(xiàn)。針對(duì)這個(gè)例子請(qǐng)注意,HibernateClinicTests
沒(méi)有包含一行代碼:
我們只需聲明@ContextConfiguration
并且測(cè)試?yán)^承于AbstractClinicTests
。
既然無(wú)需任何特定的資源位置就可以聲明@ContextConfiguration
,
那么Spring TestContext Framework就會(huì)從"AbstractClinicTests-context.xml"
(例如繼承的位置)和
"HibernateClinicTests-context.xml"
中加載應(yīng)用上下文,
同時(shí)"HibernateClinicTests-context.xml"
中定義的bean會(huì)覆蓋掉"AbstractClinicTests-context.xml"
中定義的bean。
@ContextConfiguration
public class HibernateClinicTests extends AbstractClinicTests { }
正如你在PetClinic應(yīng)用中所看到的,Spring配置文件被劃分成多個(gè)文件。對(duì)于大型應(yīng)用來(lái)說(shuō)都是這樣做的,
配置位置通常被指定在一個(gè)針對(duì)該應(yīng)用程序集成測(cè)試的通用基類(lèi)中。
這樣的基類(lèi)還可以增加有用的實(shí)例變量 - 很自然地由依賴(lài)注入組裝 - 例如使用Hibernate的應(yīng)用中的HibernateTemplate
。
從長(zhǎng)遠(yuǎn)來(lái)看,集成測(cè)試中的Spring配置文件應(yīng)該與部署環(huán)境中的一樣。一個(gè)可能的不同點(diǎn)是數(shù)據(jù)庫(kù)連接池和事務(wù)基礎(chǔ)設(shè)施。
如果你正部署到一個(gè)完整的應(yīng)用服務(wù)器上,那你可能會(huì)使用其連接池(通過(guò)JNDI訪問(wèn))和JTA實(shí)現(xiàn)。
這樣依賴(lài),在生產(chǎn)階段你會(huì)使用JndiObjectFactoryBean
來(lái)獲得DataSource
和JtaTransactionManager
。
在容器外的集成測(cè)試中無(wú)法使用JNDI和JTA,因此你應(yīng)該為他們使用一個(gè)替代的組合,
如Commons DBCP BasicDataSource
和DataSourceTransactionManager
或者HibernateTransactionManager
。
你可以將這種不同的行為放到一個(gè)單獨(dú)的XML文件中,在應(yīng)用服務(wù)器和獨(dú)立于其他配置的'本地'配置中自由選擇,這不會(huì)在測(cè)試和產(chǎn)品環(huán)境中造成差異。
此外,建議使用屬性文件來(lái)存放連接信息:請(qǐng)查看PetClinic應(yīng)用以了解這些。