使用JUnit规则进行干净的集成测试
JUnit Rules的優(yōu)勢,尤其是在進(jìn)行集成測試時,幾乎不能被高估。 在本文中,我們將闡明ExternalResource擴(kuò)展的有用性。 在我們必須使用抽象外部資源的第三方庫的情況下,這些簡化了燈具控制。 作為示例,我們將看看如何基于Git提交日志消息來驗證對條目列表的正確檢索。
什么是集成測試?
“關(guān)注點分離”可能是軟件設(shè)計和實現(xiàn)中最重要的單個概念。
語用單元測試[HUTH03]
通常,我們使用單元測試來檢查一小段生產(chǎn)代碼是否按預(yù)期工作。 但是重要的是要了解,這類測試僅限于開發(fā)人員負(fù)責(zé)的代碼。 為了澄清這一點,請考慮合并第三方庫來管理對文件,數(shù)據(jù)庫,Web服務(wù)等的訪問。
測試將隱式調(diào)用第三方組件的代碼,因為我們的被測系統(tǒng) (SUT)依賴于這些組件(DOC)[MESZ07]。 如果外部資源之一不可用,盡管開發(fā)人員的代碼可能沒有問題,但它們將失敗。 此外,訪問這些資源通常很慢,并且設(shè)置測試裝置通常很麻煩。 更不用說脆弱性了,它是由不同庫版本的潛在語義變化引起的。
所有這些缺點建議通過適配器抽象[FRPR10]將應(yīng)用程序的代碼與第三方代碼分開。 不僅僅是抽象適配器組件可以在應(yīng)用程序的問題域方面提供表達(dá)性的API,它還允許使用輕量級的替代測試double (通常表示為嘲笑)來替換基于第三方代碼的實現(xiàn)。
使用JUnit進(jìn)行測試
使用JUnit進(jìn)行測試是Java開發(fā)人員可以學(xué)習(xí)的最有價值的技能之一。 無論您的背景是什么,無論您是只是想建立一個安全網(wǎng)以減少桌面應(yīng)用程序的性能下降,還是要基于健壯且可重用的組件來提高服務(wù)器端的可靠性,都需要進(jìn)行單元測試。
弗蘭克(Frank)寫了一本書,為使用JUnit進(jìn)行測試的基本知識提供了深刻的切入點,并為您準(zhǔn)備了與測試有關(guān)的日常工作挑戰(zhàn)做好了準(zhǔn)備。
學(xué)到更多…
這消除了先前列出的有關(guān)單元測試的依賴性問題。 雙重測試的設(shè)置費用低廉,可以將測試中的系統(tǒng)與第三方代碼隔離開,并保持測試的快速和可靠[MESZ07]。 但是,它剩下的工作是測試適配器組件的正確行為。 這是集成測試開始起作用的時候。
該術(shù)語指的是軟件測試中的階段,在該階段中,將各個軟件模塊組合在一起并作為一個組進(jìn)行測試[INTTES]。 公平地說,我們使用適配器抽象將一個或多個第三方模塊組合在一起以提供某種功能。 因為從應(yīng)用程序的角度來看,這樣的適配器是低級組件,所以這種策略隱式地導(dǎo)致了一種自下而上的方法,即首先測試最低級別的組件,然后再使用它來促進(jìn)對更高級別的組件的測試。
您可能想知道為測試目的調(diào)整設(shè)計是否不是一件壞事。 但是,通過使用適配器,您可以確定應(yīng)用程序和第三方代碼之間的明確界限。 如果新的庫版本引入的行為略有不同,則只需調(diào)整適配器代碼即可再次通過相應(yīng)的集成測試。 您的實際應(yīng)用程序代碼(包括單元測試)將不受影響! 此外,您可以通過提供適當(dāng)?shù)倪m配器輕松切換到其他供應(yīng)商。 因此,遵循這種做法還可以帶來更健康的應(yīng)用程序設(shè)計。 [APPE15]
外部資源處理
不幸的是,在編寫集成測試時,我們必須面對通過使用測試倍數(shù)來避免單元測試所遇到的問題。 特別是從編碼角度來看,安裝測試夾具通常需要大量的精力。 最重要的是,我們還必須妥善保管[MESZ07]。 例如,這意味著我們可能需要在測試執(zhí)行后重置外部資源的狀態(tài)。 后者對于確保后續(xù)測試獨立運行很重要。 這樣一來,測試所做的資源修改就不會偽造其后繼者的驗證結(jié)果。
為了減少設(shè)置和拆卸代碼的重復(fù)開銷,將普通的段落交換到測試幫助程序類中似乎很自然。 考慮一下系統(tǒng)環(huán)境變量,主數(shù)據(jù)記錄等的創(chuàng)建,刪除或操作。 JUnit規(guī)則是特殊的測試助手,它像AOP框架一樣攔截測試方法調(diào)用。 與AspectJ中的環(huán)境建議相比,它們可以在實際測試執(zhí)行之前和/或之后做有用的事情。 例如,可以在測試運行之前注冊REST服務(wù)資源,并在結(jié)束后自動將其刪除。
JUnit為規(guī)則提供了方便的基類ExternalResource ,這些規(guī)則用于在測試之前設(shè)置外部資源(文件,套接字,服務(wù)器,數(shù)據(jù)庫連接等),并保證隨后將其拆除[EXRAPI]。 以下清單ServerRule顯示了原理。
public class ServerRule extends ExternalResource {private final int port;public ServerRule( int port ) {this.port = port;}@Overrideprotected void before() throws Throwable {System.out.println( "start server on port: " + port );}@Overrideprotected void after() {System.out.println( "stop server on port: " + port );} }ServerRule的構(gòu)造函數(shù)使用我們的虛擬服務(wù)器類型的端口號。 為了證明這個概念,實際上我們不啟動一個真實的,但只打印出的調(diào)用含有消息這個號碼before和after的回調(diào)掛鉤。 下一個清單顯示ServerRule的用法。
public class MyServerITest {@Rulepublic final ServerRule serverRule = new ServerRule( 5050 );@Testpublic void foo() {System.out.println( "code that fails without server access" ); } }請注意,該規(guī)則如何由帶有@Rule注釋的公共非靜態(tài)字段@Rule 。 運行測試用例將導(dǎo)致以下輸出。
start server on port: 5050 code that fails without server access stop server on port: 5050如您所見,該規(guī)則確保測試代碼在預(yù)期的環(huán)境先決條件內(nèi)執(zhí)行,并自動進(jìn)行內(nèi)部管理。 為了加深這個主題,讓我們看一個更詳細(xì)的示例,該示例說明了規(guī)則管理的燈具與被測組件之間的相互作用。
設(shè)計用于Git集成測試的規(guī)則
標(biāo)題圖像顯示了一個時間軸組件,該組件通過可配置的ItemProvider適配器檢索其Item列表。 捕獲圖片時使用的適配器類型從Git存儲庫讀取條目。 每個項目代表當(dāng)前存儲庫分支的提交。 該插圖基于我為《 Testing with JUnit》一書開發(fā)的示例應(yīng)用程序的屏幕截圖。 因為它超出了本書的范圍,所以我借此機(jī)會對我申請編寫JGit集成測試的GitRule助手進(jìn)行了解釋。
驅(qū)動程序是提供實用程序類,其目的是簡化建立包含任意提交,分支等內(nèi)容的git夾具存儲庫的任務(wù)。 為此,我創(chuàng)建了一個GitRepository類型。 這通過JGit處理本地存儲庫上的存儲庫交互。 以下摘錄應(yīng)闡明概念。
public class GitRepository {private final File location;GitRepository( File location ) {this.location = location;}public RevCommit commitFi1e( String fileName, String content, String message )throws IOException{createFi1e( fileName, content );addFi1es();return commit( message );}[...] }如您所見, GitRepository實例采用一個構(gòu)造函數(shù)參數(shù),該參數(shù)引用本地Git倉庫的工作目錄。 但是請注意構(gòu)造函數(shù)的可見性限制。 這是因為抽象不負(fù)責(zé)處理存儲庫資源的生命周期。 對于后者,我們使用ExternalResource派生,如下面的清單所示。
public class GitRule extends ExternalResource {private final Set<File> repositories;public GitRule() {repositories = new HashSet<>();}@Overrideprotected void after() {repositories.forEach( repository -> delete( repository ) );}public GitRepository create( File location ) {createRepositoryOnDisk( location );GitRepository result = new GitRepository( location );repositories.add( location);return result;}private void createRepositoryOnDisk( File location ) {InitCommand init = Git.init();init.setDirectory( location );init.setBare( false );callInit( init );}private static void callInit( InitCommand init ) {try {init.call().close();} catch( GitAPIException exception ) {throw new GitOperationException( exception );}} }GitRule可以作為工廠存儲特定測試所需的存儲庫資源。 此外,一旦完成測試執(zhí)行,它就會跟蹤正確處置所需的位置。 所示版本僅在磁盤上創(chuàng)建本地存儲庫,但是當(dāng)然可以對其進(jìn)行增強(qiáng)以克隆遠(yuǎn)程存儲庫。
ItemProvider接口依賴于擴(kuò)展類型Item的通用類型參數(shù)。 因此, GitItemProvider類型返回GitItem實例作為查找結(jié)果,并且每個git項都是JGit RevCommit的封裝。 這樣說,很明顯,第三方代碼抽象可能會影響多個類。 以下代碼段顯示了一個簡單的集成測試方案。 GitRule提供了適用于創(chuàng)建實際提交的存儲庫。 后者用于驗證GitItem實例的正確實例化。
public class GitItemTest {@Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();@Rule public final GitRule gitRule = new GitRule();@Testpublic void ofCommit() throws IOException {GitRepository repository = gitRule.create( temporaryFolder.newFolder() );RevCommit commit = repository.commitFi1e( "file", "content", "message" );GitItem actual = GitItem.ofCommit( commit );assertThat( actual ).hasId( getId( commit ) ).hasTimeStamp( getTimeStamp( commit ) ).hasContent( getContent( commit ) ).hasAuthor( getAuthor( commit ) );}[...] }該測試使用TemporaryFolder規(guī)則來確保在可訪問目錄下創(chuàng)建存儲庫。 實際上,使用臨時文件夾規(guī)則應(yīng)該使GitRule的資源刪除GitRule多余。 但是,由于它的默認(rèn)清除機(jī)制不會檢查資源刪除是否成功(無論如何,硬檢查僅適用于最新的JUnit版本),所以我選擇不依賴它。 這很重要,因為使用JGit可以很容易地遇到打開文件句柄的問題。
此外,測試的驗證是通過定制的定制GitItemAssert斷言類和一些實用程序方法(靜態(tài)導(dǎo)入)完成的。 有了這個適當(dāng)?shù)奈恢弥?#xff0c;我們準(zhǔn)備看一下更復(fù)雜的場景。
public class GitItemProviderITest {private static final String CLONE_NAME = "test";private static final int INITIAL_COMMIT_COUNT = 6;@Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();@Rule public final GitRule gitRule = new GitRule();private GitRepository repository;private GitItemProvider provider;private File remoteLocation;private File destination;@Beforepublic void setUp() throws IOException {remoteLocation = temporaryFolder.newFolder();repository = createRepository( remoteLocation );destination = temporaryFolder.newFolder();provider = new GitItemProvider( remoteLocation.toURI().toString(),destination,CLONE_NAME );}@Testpublic void fetchItems() throws IOException {int fetchCount = INITIAL_COMMIT_COUNT / 3;List<GitItem> actual = provider.fetchItems( null, fetchCount );assertThat( actual ).isEqualTo( subList( 0, fetchCount ) ).hasSize( fetchCount );}private List<GitItem> subList( int fromIndex, int toIndex ) {return repository.logAll().stream().map( commit -> ofCommit( commit ) ).collect( toList() ).subList( fromIndex, toIndex );}[...] }設(shè)置與之前的測試相似。 但是,我們的裝置存儲庫是通過委派給createRepository方法來創(chuàng)建的。 為了簡潔起見,我在此省略了詳細(xì)信息,因為該方法僅創(chuàng)建具有INITIAL_COMMIT_COUNT提交的存儲庫。 被測試的GitItemProvider組件采用三個構(gòu)造函數(shù)參數(shù)。 第一個是夾具庫的位置,它將由提供者克隆。 為此,第二個參數(shù)定義一個目標(biāo)目錄,而第三個參數(shù)將插入克隆存儲庫的文件夾名稱。
在練習(xí)階段,組件從其克隆的存儲庫中獲取可用提交的子集。 subList驗證該列表,該列表是由我們的燈具存儲庫中的方法subList計算得出的預(yù)期列表。 最后,這些規(guī)則負(fù)責(zé)客房整理。
如果要查看完整的示例代碼,請參閱GitHub存儲庫https://github.com/fappel/Testing-with-JUnit上可用的示例應(yīng)用程序源。
摘要
這篇文章介紹了如何在編寫集成測試時將JUnit規(guī)則用于干凈的資源管理。 我們已經(jīng)對什么是集成測試有了基本的了解,了解了ExternalResource測試實用程序擴(kuò)展的工作原理,并詳細(xì)說明了使用示例。 當(dāng)然,它的意義不僅僅在于初見。 熟悉此處顯示的原理后,您可能會考慮研究其他主題,例如使用ClassRule來處理持久性固定裝置, 規(guī)則鏈 , 環(huán)境變量等。
不能不告訴您我的書《 使用JUnit進(jìn)行測試 》中的第6章“ 使用JUnit規(guī)則減少樣板”可作為免費閱讀樣本, 網(wǎng)址為https://www.packtpub.com/packtlib/book/Application%20Development/ 9781782166603/6 。 如果您還不厭倦我的麻煩,請大膽前進(jìn)并抓住機(jī)會深入研究JUnit規(guī)則的世界……
因此,請記住,人們始終遵守規(guī)則–不要忘記分享知識&#55357;&#56841;
資源資源
- [APPE15]:Appel, 使用JUnit測試 ,Packt Publishing,2015年
- [EXRAPI]:ExternalResource,API DOC, http ://junit.org/apidocs/org/junit/rules/ExternalResource.html
- [FRPR10]:Freeman,Pryce, 不斷發(fā)展的面向?qū)ο筌浖?#xff0c;由Tests指導(dǎo) ,Addison Wesley,2010年
- [HUTH03]:Hunt,Thomas, 實用單元測試有限公司,2003年,2004年
- [INTTES]:Wikipedia,IntegrationTesting, https ://en.wikipedia.org/wiki/Integration_testing
- [MESZ07]:Meszaros, xUnit測試模式 ,Pearson Education,Inc.,2007年
翻譯自: https://www.javacodegeeks.com/2015/09/clean-integration-testing-with-junit-rules-3.html
總結(jié)
以上是生活随笔為你收集整理的使用JUnit规则进行干净的集成测试的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ps颜色叠加快捷键(PS图案叠加快捷键)
- 下一篇: 鹈鹕将帮助史蒂芬森恢复 痊愈后或重签他