JUnit规则
第一次偶然發(fā)現(xiàn)JUnit @Rule批注時,我對此概念有些惱火。 在測試用例中擁有一個公共領(lǐng)域似乎有些奇怪,因此我不愿意定期使用它。 但是一段時間后,我習(xí)慣了這一點,事實證明,規(guī)則可以通過多種方式簡化編寫測試的過程。 這篇文章簡要介紹了該概念,并簡要列舉了一些規(guī)則的優(yōu)點。
什么是JUnit規(guī)則?
讓我們從一個現(xiàn)成的JUnit規(guī)則開始。 TemporaryFolder是一個測試幫助程序,可用于為臨時內(nèi)容1創(chuàng)建位于文件系統(tǒng)目錄下的文件和文件夾。 TemporaryFolder的有趣之處在于,它保證在測試方法完成時刪除其文件和文件夾2 。 為了按預(yù)期方式工作,必須將臨時文件夾實例分配給@Rule注釋字段,該字段必須是公共的(不是靜態(tài)的),并且是TestRule的子類型:
public class MyTest {@Rulepublic TemporaryFolder temporaryFolder = new TemporaryFolder();@Testpublic void testRun() throws IOException {assertTrue( temporaryFolder.newFolder().exists() );} }
它是如何工作的?
規(guī)則提供了一種攔截測試方法調(diào)用的可能性,就像AOP框架一樣。 與AspectJ中的周圍建議相比,您可以在實際測試執(zhí)行之前和/或之后做一些有用的事情3 。 盡管這聽起來很復(fù)雜,但是卻很容易實現(xiàn)。
規(guī)則定義的API部分必須實現(xiàn)TestRule。 此接口稱為apply的唯一方法返回Statement 。 Statement s表示(簡單地說)在JUnit運行時中的測試,而Statement#evaluate()執(zhí)行它們。 現(xiàn)在,基本思想是提供Statement包裝擴展,該包裝可以通過覆蓋Statement#evaluate()來進行實際貢獻:
public class MyRule implements TestRule {@Overridepublic Statement apply( Statement base, Description description ) {return new MyStatement( base );} }public class MyStatement extends Statement {private final Statement base;public MyStatement( Statement base ) {this.base = base;}@Overridepublic void evaluate() throws Throwable {System.out.println( 'before' );try {base.evaluate();} finally {System.out.println( 'after' );}} }MyStatement作為包裝器實現(xiàn),在MyRule#apply(Statement,Destination)使用該包裝器包裝作為參數(shù)給出的原始語句。 很容易看出,包裝程序覆蓋了Statement#evaluate()在實際測試4之前和之后做一些事情。
下一個代碼片段顯示如何與上面的TemporaryFolder完全一樣地使用MyRule :
public class MyTest {@Rulepublic MyRule myRule = new MyRule();@Testpublic void testRun() {System.out.println( 'during' );} }啟動測試用例將導(dǎo)致以下控制臺輸出,這證明我們的示例規(guī)則可以按預(yù)期工作。 測試執(zhí)行被我們的規(guī)則攔截和修改,以在測試的“期間”前后打印“之前”和“之后”:
before during after現(xiàn)在已經(jīng)了解了基礎(chǔ)知識,下面讓我們看一下您可以使用規(guī)則執(zhí)行的更有用的事情。
測試治具
從相應(yīng)的維基百科部分引用的“測試裝置”是運行測試并期望獲得特定結(jié)果所必須具備的所有條件。 通常,通過處理單元測試框架的setUp()和tearDown()事件來創(chuàng)建固定裝置。
使用JUnit,這通常看起來像這樣:
public class MyTest {private MyFixture myFixture;@Testpublic void testRun1() {myFixture.configure1();// do some testing here}@Testpublic void testRun2() {myFixture.configure2();// do some testing here}@Beforepublic void setUp() {myFixture = new MyFixture();}@Afterpublic void tearDown() {myFixture.dispose();} }考慮您在許多測試中以上面顯示的方式使用特定的夾具。 在那種情況下,最好擺脫setUp()和tearDown()方法。 鑒于以上各節(jié),我們現(xiàn)在知道可以通過更改MyFixture來實現(xiàn)TestRule來完成。 適當(dāng)?shù)腟tatement實現(xiàn)必須確保它調(diào)用MyFixture#dispose()并且看起來可能像這樣:
public class MyFixtureStatement extends Statement {private final Statement base;private final MyFixture fixture;public MyFixtureStatement( Statement base, MyFixture fixture ) {this.base = base;this.fixture = fixture;}@Overridepublic void evaluate() throws Throwable {try {base.evaluate();} finally {fixture.dispose();}} }有了這個,上面的測試可以重寫為:
public class MyTest {@Rulepublic MyFixture myFixture = new MyFixture();@Testpublic void testRun1() {myFixture.configure1();// do some testing here}@Testpublic void testRun2() {myFixture.configure2();// do some testing here} }在很多情況下,我開始欣賞使用規(guī)則編寫測試的更為緊湊的形式,但是可以肯定的是,這也是一個品味問題以及您認為更適合閱讀的內(nèi)容5 。
帶有方法注釋的夾具配置
到目前為止,我已默默地忽略了TestRule#apply(Statement,Description)的Description參數(shù)。 通常, Description描述了將要運行或已經(jīng)運行的測試。 但它也允許訪問有關(guān)底層java方法的一些反射信息。 除其他外,有可能讀取這種方法附帶的注釋。 這使我們能夠?qū)⒁?guī)則與方法注釋結(jié)合起來,以方便配置TestRule 。
考慮以下注釋類型:
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Configuration {String value(); }與MyFixture#apply(Statement,Destination)中的以下代碼段結(jié)合使用,該代碼讀取注釋為特定測試方法的配置值…
Configuration annotation= description.getAnnotation( Configuration.class ); String value = annotation.value(); // do something useful with value…上面演示MyFixture規(guī)則用法的MyFixture可以重寫為:
public class MyTest {@Rulepublic MyFixture myFixture = new MyFixture();@Test@Configuration( value = 'configuration1' )public void testRun1() {// do some testing here}@Test@Configuration( value = 'configuration2' )public void testRun2() {// do some testing here} }當(dāng)然,由于注釋僅允許Enum , Class es或String文字作為參數(shù),因此后一種方法存在局限性。 但是在某些用例中,這已經(jīng)足夠了。 restfuse庫提供了一個很好的示例,該示例將規(guī)則與方法注釋結(jié)合使用。 如果您對現(xiàn)實世界的示例感興趣,則應(yīng)查看Destination規(guī)則6的庫實現(xiàn)。
最后,剩下的唯一要說的是,我很想聽聽您關(guān)于可以用來簡化日常測試工作的JUnit規(guī)則的其他有用示例的信息:
參考:來自JCG合作伙伴 Frank Appel的JUnit規(guī)則 ,位于Code Affine博客上。
翻譯自: https://www.javacodegeeks.com/2012/11/junit-rules.html
總結(jié)
- 上一篇: 韩国现代手机官网(韩国现代科技中国有限公
- 下一篇: 高级ZK:异步UI更新和后台处理–第2部