junit5 动态测试_JUnit 5 –动态测试
junit5 動態(tài)測試
在定義測試時,JUnit 4有一個很大的弱點:它必須在編譯時發(fā)生。 現(xiàn)在,JUnit 5將解決此問題! Milestone 1 剛剛發(fā)布 ,它帶有全新的動態(tài)測試,可以在運行時創(chuàng)建測試。
總覽
本系列中有關(guān)JUnit 5的其他文章:
- 建立
- 基本
- 建筑
- 擴展模型
- 條件
- 注射
- 動態(tài)測試
- …
本系列基于預發(fā)行版本Milestone 1 ,當然會隨時更改。 發(fā)布新的里程碑或常規(guī)發(fā)布版本時,帖子將更新。
您將在此處閱讀的大多數(shù)內(nèi)容和更多內(nèi)容都可以在新興的JUnit 5用戶指南中找到 (該鏈接已鏈接到Milestone 1版本–您可以在此處找到最新版本)。 我在這里顯示的代碼示例可以在GitHub上找到 。
靜態(tài)測試
JUnit 3通過分析方法名稱并檢查它們是否以測試開頭來識別測試。 JUnit 4利用了(后來的)注解,并引入了@Test,它給了我們更多的自由。 這兩種技術(shù)共享相同的方法:測試是在編譯時定義的。
但是,事實證明這是相當有限的。 例如,考慮一種常見情況,即應該對多種輸入數(shù)據(jù)執(zhí)行相同的測試,在這種情況下,應針對許多不同點:
void testDistanceComputation(Point p1, Point p2, double distance) {assertEquals(distance, p1.distanceTo(p2)); }我們有什么選擇? 最直接的方法是創(chuàng)建許多有趣的點,然后在循環(huán)中調(diào)用我們的測試方法:
@Test void testDistanceComputations() {List<PointPointDistance> testData = createTestData();for (PointPointDistance datum : testData) {testDistanceComputation(datum.point1(), datum.point2(), datum.distance());} }但是,如果這樣做,JUnit會將循環(huán)視為單個測試。 這意味著測試僅在第一個失敗之前執(zhí)行,報告會受到影響,并且工具支持通常不及格。
有幾個JUnit 4功能和擴展可解決此問題。 它們或多或少都可以工作,但通常限于特定的用例( Theories ),使用起來很笨拙( Parameterized ),并且通常需要運行程序(值得稱贊的JUnitParams )。 原因是它們都受到相同的限制:JUnit 4確實不支持在運行時創(chuàng)建測試。
使用lambda創(chuàng)建測試也是如此。 有些人想定義這樣的測試:
class PointTest {"Distance To Origin" -> {Point origin = Point.create(0,0);Point p = Point.create(3,4);assertEquals(5, origin.distanceTo(p));}}當然,這只是一個理想選擇-它甚至無法在Java中進行編譯。 盡管如此,看到我們能達到多近還是很有趣的。 las,也無法靜態(tài)標識各個lambda,因此此處也有相同的限制。
但是,如果JUnit 5沒有提出解決方案,我不會寫所有這些內(nèi)容:動態(tài)測試進行救援!
發(fā)布時間由NASA戈達德太空飛行中心在CC-BY-SA 2.0
動態(tài)測試
從最近開始,JUnit 5代碼庫就采用了新的類型和新的注釋,它們共同解決了我們的問題。
首先,有DynamicTest ,它是測試的簡單包裝。 它有一個名稱,并保存構(gòu)成測試主體的代碼。 后者以Executable的形式發(fā)生,就像Runnable但是可以拋出任何Throwable (可塑命名)。 它是使用靜態(tài)工廠方法創(chuàng)建的:
public static DynamicTest dynamicTest(String name, Executable test);然后是@TestFactory ,可以注釋方法。 這些方法必須返回動態(tài)測試的Iterator , Iterable或Stream 。 (這當然不能在編譯時強制執(zhí)行,因此,如果我們返回其他內(nèi)容,JUnit將在運行時發(fā)出barf。)
很容易看出他們是如何合作的:
因此,我們能夠在運行時動態(tài)創(chuàng)建測試:
@TestFactory List<DynamicTest> createPointTests() {return Arrays.asList(DynamicTest.dynamicTest("A Great Test For Point",() -> {// test code}),DynamicTest.dynamicTest("Another Great Test For Point",() -> {// test code})); }讓我們看看如何使用它來解決我們上面描述的問題。
要創(chuàng)建參數(shù)化測試,我們執(zhí)行與之前非常相似的操作:
@TestFactory Stream<DynamicTest> testDistanceComputations() {List<PointPointDistance> testData = createTestData();return testData.stream().map(datum -> DynamicTest.dynamicTest("Testing " + datum,() -> testDistanceComputation(datum.point1(), datum.point2(), datum.distance()))); }與上面所做的操作的關(guān)鍵區(qū)別在于,我們不再直接執(zhí)行testDistanceComputation 。 取而代之的是,我們?yōu)槊總€數(shù)據(jù)創(chuàng)建一個動態(tài)測試,這意味著JUnit將知道這些測試很多,而不僅僅是一個。
在這種情況下,我們可能會使用其他方法來生成動態(tài)測試:
@TestFactory Stream<DynamicTest> testDistanceComputations() {return DynamicTest.stream(createTestData().iterator(),datum -> "Testing " + datum,datum -> testDistanceComputation(datum.point1(), datum.point2(), datum.distance())); }在這里,我們將測試數(shù)據(jù)傳遞給stream ,然后告訴它如何從中創(chuàng)建名稱和測試。
所以你怎么看? 也許符合“ JUnit 5將這些作為單獨的測試來對待,但是從語法上來看仍然很麻煩”的思路嗎? 好吧,至少我是這樣認為的。 該功能很好,但有點笨拙。
但這只是里程碑1,因此有足夠的時間進行改進。 也許擴展可以提供一種更舒適的方式來創(chuàng)建動態(tài)測試,但是我不太清楚如何。 我想, 新的擴展點會有所幫助。
Lambda測試
好吧,讓我們看看我們距離備受追捧的lambda測試有多近。 現(xiàn)在,并未為此明確創(chuàng)建動態(tài)測試,因此我們必須進行一些修改。 (這種修補是由Jens Schauder 關(guān)于JUnit 5的演講之一“錯誤地啟發(fā)”的。感謝Jens!)
動態(tài)測試需要一個名稱和一個可執(zhí)行文件,用lambda創(chuàng)建后者聽起來很合理。 為了能夠做到這一點,我們需要一個目標,即lambda被分配給的目標。 想到一個方法參數(shù)...
但是該方法會做什么? 顯然,它應該創(chuàng)建一個動態(tài)測試,然后呢? 也許我們可以將該測試轉(zhuǎn)儲到某個地方,然后讓JUnit進行測試?
public class LambdaTest {private final List<DynamicTest> tests = new ArrayList<>();// use lambda to create the 'Executable'public void registerTest(String name, Executable test) {tests.add(DynamicTest.dynamicTest(name, test));}@TestFactoryvoid List<DynamicTest> tests() {return tests;}}好的,這看起來很有希望。 但是,我們從哪里獲得LambdaTest的實例? 對于我們的測試類,最簡單的解決方案是簡單地對其進行擴展,然后重復調(diào)用registerTest 。 如果這樣做的話,我們可能更喜歡一個短名稱。 我們還可以使其受到保護:
// don't do this at home! protected void λ(String name, Executable test) {tests.add(DynamicTest.dynamicTest(name, test)); }看來我們要到達那里。 剩下的就是調(diào)用λ了,唯一顯而易見的方法是從測試類的構(gòu)造函數(shù)內(nèi)部進行:
class PointTest extends LambdaTest {public PointTest() {λ("A Great Test For Point", () -> {// test code})}}我們已經(jīng)完成修補工作。 為了進一步發(fā)展,我們必須開始黑客攻擊。 有沒有聽說過雙括號初始化 ? 這是一個有點奇怪的功能,它創(chuàng)建一個匿名子類并在新類的構(gòu)造函數(shù)中執(zhí)行給定的代碼。 有了它,我們可以走得更遠:
class PointTest extends LambdaTest {{λ("A Great Test For Point", () -> {// test code});}}如果我們真的很渴望,我們可以刪除另外兩個符號。 有了這個怪異的技巧 (我們現(xiàn)在受到Benji Weber的啟發(fā)),我們可以通過反射確定lambda的參數(shù)名稱,并將其用作測試的名稱。 為了利用這一點,我們需要一個新的接口,并且必須稍微更改LambdaTest ::λ:
@FunctionalInterface // the interface we are extending here allows us // to retrieve the parameter name via 'prettyName()' // (the black magic is hidden inside that method; // look at 'MethodFinder' and 'NamedValue' in Benji's post) public interface NamedTest extends ParameterNameFinder {void execute(String name); }protected void λ(NamedTest namedTest) {String name = namedTest.prettyName();Executable test = () -> namedTest.execute(name);tests.add(DynamicTest.dynamicTest(name, test)); }總而言之,我們可以創(chuàng)建如下測試:
class PointTest extends LambdaTest {{λ(A_Great_Test_For_Point -> {// test code});}}你怎么看? 所有這些黑客值得嗎? 老實說,我不介意讓我的IDE生成測試方法樣板,所以我的回答是“否”。 但這是一個有趣的實驗。 :)
生命周期
動態(tài)測試的當前實現(xiàn)是故意的。 這種顯示方式之一是它們沒有集成到生命周期中。 從用戶指南中:
這意味著對于動態(tài)測試,不會執(zhí)行@BeforeEach和@AfterEach方法及其對應的擴展回調(diào)。 換句話說,如果您在lambda表達式中訪問來自測試實例的字段以進行動態(tài)測試,則這些字段將不會由回調(diào)方法或由同一@TestFactory方法生成的動態(tài)測試在執(zhí)行之間的擴展名進行重置。
不過,已經(jīng)有一個問題可以解決 。
反射
那我們看到了什么? 到目前為止,JUnit只知道在編譯時聲明的測試。 JUnit 5具有動態(tài)測試的概念,動態(tài)測試是在運行時創(chuàng)建的,由名稱和保存測試代碼的可執(zhí)行文件組成。 到此為止,我們已經(jīng)看到了如何創(chuàng)建參數(shù)化測試以及如何使用lambda來以更現(xiàn)代的風格定義測試。
你怎么看? 渴望嘗試嗎?
翻譯自: https://www.javacodegeeks.com/2016/07/junit-5-dynamic-tests.html
junit5 動態(tài)測試
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的junit5 动态测试_JUnit 5 –动态测试的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 百度网盘离线下载错误36036什么意思?
- 下一篇: 生活中如何将手机视频传到电脑上如何把视频