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