扩展 junit 框架_JUnit 5 –扩展模型
擴展 junit 框架
我們已經(jīng)對Java最普遍的測試框架的下一個版本了解很多。 現(xiàn)在,讓我們看一下JUnit 5擴展模型,該模型將允許庫和框架將自己的實現(xiàn)添加到JUnit中。
總覽
- 建立
- 基本
- 建筑
- 擴展模型
- 條件
- 注射
- …
在新興的《 JUnit 5用戶指南》中可以找到您將在此處閱讀的更多內容以及更多內容。 請注意,它基于Alpha版本,因此可能會發(fā)生變化。
確實,我們鼓勵我們提出問題或提出請求,以便JUnit 5可以進一步改進。 請利用這個機會! 這是我們幫助JUnit幫助我們的機會,因此,如果您能在這里看到一些改善,請確保將其上游 。
如有必要,此帖子將得到更新。 我在這里顯示的代碼示例可以在GitHub上找到 。
JUnit 4擴展模型
首先讓我們看一下JUnit 4是如何解決該問題的。 它具有兩個部分競爭的擴展機制:運行程序和規(guī)則。
跑步者
測試運行者管理測試的生命周期:實例化,調用設置和拆卸方法,運行測試,處理異常,發(fā)送通知等。JUnit4提供了實現(xiàn)所有這些功能的實現(xiàn)。
在4.0中,只有一種擴展JUnit的方法:創(chuàng)建一個新的運行器并使用@RunWith(MyRunner.class)注釋測試類,以便JUnit使用它而不是其自己的實現(xiàn)。
該機制非常繁重,并且擴展范圍很小。 而且它有一個非常嚴格的限制:每個測試班級只能有一個跑步者,這使得他們無法組成。 因此,無法同時利用Mockito和Spring跑步者的功能。
規(guī)則
為了克服這些限制,JUnit 4.7引入了rules ,它們是測試類的帶注釋字段。 JUnit 4將測試方法(和其他操作)包裝到一條語句中,并將其傳遞給規(guī)則。 然后,他們可以在執(zhí)行語句之前和之后執(zhí)行一些代碼。 此外,測試方法通常在執(zhí)行期間在規(guī)則實例上調用方法。
一個示例是臨時文件夾規(guī)則 :
public static class HasTempFolder {@Rulepublic TemporaryFolder folder= new TemporaryFolder();@Testpublic void testUsingTempFolder() throws IOException {File createdFile= folder.newFile("myfile.txt");File createdFolder= folder.newFolder("subfolder");// ...} }由于使用@Rule批注,JUnit調用帶有包裝方法testUsingTempFolder的語句的文件夾 。 編寫此特定規(guī)則是為了使文件夾創(chuàng)建一個臨時文件夾,執(zhí)行測試,然后再刪除該文件夾。 然后,測試本身可以在臨時文件夾中創(chuàng)建文件和文件夾。
其他規(guī)則可能會在Swing的事件分發(fā)線程中運行測試 ,建立和拆除數(shù)據(jù)庫,或者如果測試運行時間過長,則讓測試超時 。
規(guī)則是一個很大的改進,但是通常僅限于在測試運行之前和之后執(zhí)行一些代碼。 他們無法幫助無法在該框架內實現(xiàn)的擴展。
事態(tài)
JUnit有兩種相互競爭的擴展機制,每種都有其自身的局限性。
因此,自JUnit 4.7起,就有兩種競爭的擴展機制,每種都有其自身的局限性,但也有很多重疊之處。 這使得干凈擴展很困難。 此外,編寫不同的擴展可能會出現(xiàn)問題,并且通常無法實現(xiàn)開發(fā)人員希望的擴展。
由Tony Walmsley在CC-BY 2.0下發(fā)布
JUnit 5擴展模型
JUnit Lambda項目具有兩個核心原則 ,其中之一是“優(yōu)先于功能而不是擴展點”。 從字面上看,這轉化為新版本的整體機制–這不僅是擴展JUnit 5的唯一機制,也是最重要的機制。
延伸點
JUnit 5擴展可以聲明對測試生命周期的某些特定時刻感興趣。 當JUnit 5引擎處理測試時,它將逐步通過這些步驟并調用每個已注冊的擴展。 從外觀上看,這些是擴展點:
- 測試實例后處理
- 之前回調
- 有條件的測試執(zhí)行
- 每次回調之前
- 參數(shù)解析
- 異常處理
- AfterEach回調
- 畢竟回調
(不要擔心它們是否每個都不清楚。我們稍后會介紹其中的一些。)
每個擴展點對應一個接口。 他們的方法采用的參數(shù)可以捕獲測試生命周期中特定點的上下文,例如測試實例和方法,測試名稱,參數(shù),注釋等。
擴展可以實現(xiàn)任何數(shù)量的那些接口,并且將由引擎使用相應的參數(shù)進行調用。 然后,它可以執(zhí)行實現(xiàn)其功能所需的任何操作。 需要考慮的一個細節(jié):引擎在實例化擴展時以及將實例保留多長時間時不做任何保證,因此它們必須是無狀態(tài)的。 他們需要維護的任何狀態(tài)都必須寫入JUnit并從中加載。
創(chuàng)建擴展后,剩下要做的就是告訴JUnit。 這是那么容易,因為添加@ExtendWith(MyExtension。 類 ),需要延長測試類或方法。
實際上,存在一個稍微不那么冗長和更多顯示的選項。 但是為此,我們首先必須看看JUnit擴展模型的另一個Struts。
自定義注釋
JUnit 5 API由注釋驅動,當引擎檢查它們的存在時,它會做一些額外的工作:它不僅在類,方法和參數(shù)上查找注釋,還在其他注釋上查找。 并且它將發(fā)現(xiàn)的所有內容都視為立即存在于所檢查的元素上。 注釋可以通過所謂的meta-annotations進行注釋 ,很酷的是,所有JUnit注釋都是完全meta的。
這樣就可以輕松創(chuàng)建和編寫在JUnit 5中完全可用的注釋:
/*** We define a custom annotation that:* - stands in for '@Test' so that the method gets executed* - has the tag "integration" so we can filter by that,* e.g. when running tests from the command line*/ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Test @Tag("integration") public @interface IntegrationTest { }然后我們可以像這樣使用它:
@IntegrationTest void runsWithCustomAnnotation() {// this gets executed// even though `@IntegrationTest` is not defined by JUnit }或者我們可以為擴展創(chuàng)建更簡潔的注釋:
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @ExtendWith(ExternalDatabaseExtension.class) public @interface Database { }現(xiàn)在我們可以使用@Database代替@ExtendWith(ExternalDatabaseExtension。 類 )。 由于我們添加了ElementType 。 ANNOTATION_TYPE到允許的目標列表中,它也是一個元注釋,我們或其他人可以對其進行進一步的組合。
假設我們要對某些測試的運行時間進行基準測試。 首先,我們創(chuàng)建要使用的注釋:
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @ExtendWith(BenchmarkCondition.class) public @interface Benchmark { }它已經(jīng)指向BenchmarkCondition ,我們將在接下來實現(xiàn)。 這是我們的計劃:
- 衡量整個測試類的運行時間,存儲執(zhí)行任何測試之前的時間
- 衡量各個測試方法的運行時間,存儲每次測試之前的時間
- 執(zhí)行測試方法后,檢索測試的啟動時間,計算并打印結果運行時
- 執(zhí)行完所有測試后,檢索類的啟動時間,計算并打印結果運行時
- 僅當使用@Benchmark注釋類或方法時,才執(zhí)行任何此操作
最后一點可能不會立即顯而易見。 為什么擴展名不會處理未用@Benchmark注釋的方法? 這源于以下事實:如果將擴展應用于類,它將自動應用于其中的所有方法。 因此,如果我們的要求表明我們可能希望對類進行基準測試,但不一定要對所有單個方法進行基準測試,則需要排除它們。 我們通過檢查它們是否被單獨注釋來做到這一點。
碰巧的是,前四個點直接對應于生命周期回調BeforeAll , BeforeEach , AfterEach , AfterAll ,因此我們要做的就是實現(xiàn)四個對應的接口。 這些實現(xiàn)非常簡單,它們只是按照我們上面所說的去做:
public class BenchmarkCondition implementsBeforeAllExtensionPoint, BeforeEachExtensionPoint,AfterEachExtensionPoint, AfterAllExtensionPoint {private static final Namespace NAMESPACE =Namespace.of("BenchmarkCondition");@Overridepublic void beforeAll(ContainerExtensionContext context) {if (!shouldBeBenchmarked(context))return;writeCurrentTime(context, LaunchTimeKey.CLASS);}@Overridepublic void beforeEach(TestExtensionContext context) {if (!shouldBeBenchmarked(context))return;writeCurrentTime(context, LaunchTimeKey.TEST);}@Overridepublic void afterEach(TestExtensionContext context) {if (!shouldBeBenchmarked(context))return;long launchTime = loadLaunchTime(context, LaunchTimeKey.TEST);long runtime = currentTimeMillis() - launchTime;print("Test", context.getDisplayName(), runtime);}@Overridepublic void afterAll(ContainerExtensionContext context) {if (!shouldBeBenchmarked(context))return;long launchTime = loadLaunchTime(context, LaunchTimeKey.CLASS);long runtime = currentTimeMillis() - launchTime;print("Test container", context.getDisplayName(), runtime);}private static boolean shouldBeBenchmarked(ExtensionContext context) {return context.getElement().isAnnotationPresent(Benchmark.class);}private static void writeCurrentTime(ExtensionContext context, LaunchTimeKey key) {context.getStore(NAMESPACE).put(key, currentTimeMillis());}private static long loadLaunchTime(ExtensionContext context, LaunchTimeKey key) {return (Long) context.getStore(NAMESPACE).remove(key);}private static void print(String unit, String displayName, long runtime) {System.out.printf("%s '%s' took %d ms.%n", unit, displayName, runtime);}private enum LaunchTimeKey {CLASS, TEST} }有趣的細節(jié)是shouldBeBenchmarked ,它使用JUnit的API毫不費力地確定當前元素是否被@Benchmark (元)注釋,以及writeCurrentTime / loadLaunchTime ,后者使用存儲來寫入和讀取啟動時間。
- 您可以在GitHub上找到代碼 。
下一篇文章將討論條件測試執(zhí)行和參數(shù)注入,并顯示有關如何使用相應擴展點的示例。 如果您迫不及待,請查看這篇文章 ,其中展示了如何將兩個JUnit 4規(guī)則(條件禁用和臨時文件夾)移植到JUnit 5。
摘要
我們已經(jīng)看到,JUnit 4的運行者和規(guī)則對于創(chuàng)建干凈,強大且可組合的擴展不是理想的選擇。 JUnit 5旨在通過更通用的擴展點概念來克服它們的局限性。 它們允許擴展程序指定要在測試生命周期中的哪些時間點進行干預。 我們還研究了元注釋如何使輕松創(chuàng)建自定義注釋成為可能。
你怎么看?
翻譯自: https://www.javacodegeeks.com/2016/04/junit-5-extension-model.html
擴展 junit 框架
總結
以上是生活随笔為你收集整理的扩展 junit 框架_JUnit 5 –扩展模型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: junit测试起名字规则_如何在JUni
- 下一篇: cad笔记本电脑配置推荐2021?