您的位置:軟件測(cè)試 > 開(kāi)源軟件測(cè)試 > 開(kāi)源單元測(cè)試工具 > junit
JUnit 5系列之基礎(chǔ)入門(mén)介紹
作者:Linesh 發(fā)布時(shí)間:[ 2016/9/29 14:32:34 ] 推薦標(biāo)簽:單元測(cè)試 Junit

  上周我們剛剛搭建好了 JUnit 5 的環(huán)境,現(xiàn)在我們可以寫(xiě)測(cè)試了。這節(jié)讓我們來(lái)寫(xiě)它幾個(gè)吧!
  概述
  本文章是這個(gè) JUnit 5 系列的一部分:
  · 環(huán)境搭建
  · 基礎(chǔ)入門(mén)
  · 架構(gòu)體系
  · 擴(kuò)展模型(Extension Model)
  · 條件斷言
  · 注入
  · 動(dòng)態(tài)測(cè)試
  ...
  (如果不喜歡看文章,你可以戳這里看我的演講,或者看一下近的 vJUG 講座,或者我在 DevoxxPL 上的 PPT。
  本系列文章都基于 Junit 5發(fā)布的先行版 Milestone 2。它可能會(huì)有變化。如果有新的里程碑(milestone)版本發(fā)布,或者試用版正式發(fā)行時(shí),我會(huì)再來(lái)更新這篇文章。
  這里要介紹的多數(shù)知識(shí)你都可以在 JUnit 5 用戶指南 中找到(這個(gè)鏈接指向的是先行版 Milestone 2,想看的新版本文檔的話請(qǐng)戳這里),并且指南還有更多的內(nèi)容等待你發(fā)掘。下面的所有代碼都可以在 我的 Github 上找到。
  設(shè)計(jì)哲學(xué)
  新的架構(gòu)設(shè)計(jì)(這個(gè)我們?nèi)蘸罅?,其關(guān)注點(diǎn)在高擴(kuò)展性。如果后面出現(xiàn)了什么神之測(cè)試技術(shù)(至少對(duì)我們廣大 Java?來(lái)說(shuō)很神的),它們也可能在 JUnit 5 的架構(gòu)下被實(shí)現(xiàn)。
  不過(guò)當(dāng)前來(lái)說(shuō),涉及的基礎(chǔ)知識(shí)與 JUnit 4 是非常相似的。JUnit 5 的改動(dòng)并不激進(jìn),相反它的優(yōu)化歷程是小心翼翼,小步迭代的。因此,開(kāi)發(fā)者應(yīng)該會(huì)對(duì)新的 API 感到非常熟悉。至少我是這樣的,我相信你也不會(huì)感覺(jué)陌生:
class Lifecycle {
@BeforeAll
static void initializeExternalResources() {
System.out.println("Initializing external resources...");
}
@BeforeEach
void initializeMockObjects() {
System.out.println("Initializing mock objects...");
}
@Test
void someTest() {
System.out.println("Running some test...");
assertTrue(true);
}
@Test
void otherTest() {
assumeTrue(true);
System.out.println("Running another test...");
assertNotEquals(1, 42, "Why wouldn't these be the same?");
}
@Test
@Disabled
void disabledTest() {
System.exit(1);
}
@AfterEach
void tearDown() {
System.out.println("Tearing down...");
}
@AfterAll
static void freeExternalResources() {
System.out.println("Freeing external resources...");
}
}
  是吧?這里并沒(méi)有很大的改動(dòng)。
  JUnit 5 預(yù)備
  包可見(jiàn)性
  JUnit 5 明顯的變化應(yīng)該是,不再需要手動(dòng)將測(cè)試類與測(cè)試方法為 public 了。包可見(jiàn)的訪問(wèn)級(jí)別足夠了。當(dāng)然,私有(private)訪問(wèn)還是不行的。我認(rèn)為這個(gè)變化是合理的,也符合我們對(duì)可見(jiàn)性的一般直覺(jué)。
  這很好!至少可以少打幾個(gè)字母了。不過(guò),我相信你也不是每次都手打這幾個(gè)字母的,是吧?盡管如此還是很好,少一些關(guān)鍵字,你在看測(cè)試的時(shí)候也少些切換。
  測(cè)試的生命周期
  @Test
  JUnit 中基本的注解非 @Test 莫屬了。它會(huì)標(biāo)記方法為測(cè)試方法,以便構(gòu)建工具和 IDE 能夠識(shí)別并執(zhí)行它們。
  它的 API 和作用并沒(méi)有變化,不過(guò)它不再接受任何參數(shù)了。若要測(cè)試是否拋出異常,你可以通過(guò)新的斷言 API 來(lái)做到;不過(guò)我所知,目前還沒(méi)有超時(shí)選項(xiàng)timeout的替代品。
  與 JUnit 4一樣,JUnit 5 會(huì)為每個(gè)測(cè)試方法創(chuàng)建一個(gè)新的實(shí)例。
  Before 和 After
  你可能需要執(zhí)行一些代碼來(lái)在測(cè)試執(zhí)行前后完成一些初始化或銷毀的操作。在 JUnit 5 中,有4個(gè)注解你可能會(huì)用于如此工作:
  @BeforeAll
  只執(zhí)行一次,執(zhí)行時(shí)機(jī)是在所有測(cè)試和 @BeforeEach 注解方法之前。
  @BeforeEach
  在每個(gè)測(cè)試執(zhí)行之前執(zhí)行。
  @AfterEach
  在每個(gè)測(cè)試執(zhí)行之后執(zhí)行。
  @AfterAll
  只執(zhí)行一次,執(zhí)行時(shí)機(jī)是在所有測(cè)試和 @AfterEach 注解方法之后。
  因?yàn)榭蚣軙?huì)為每個(gè)測(cè)試創(chuàng)建一個(gè)單獨(dú)的實(shí)例,在 @BeforeAll/@AfterAll 方法執(zhí)行時(shí)尚無(wú)任何測(cè)試實(shí)例誕生。因此,這兩個(gè)方法必須定義為靜態(tài)方法。
  注解了同樣一個(gè)注解的不同方法,其執(zhí)行次序是不可預(yù)知的,包括對(duì)繼承來(lái)的方法也適用。這是開(kāi)發(fā)團(tuán)隊(duì)經(jīng)過(guò)審慎思考后的決定,即把單元測(cè)試與集成測(cè)試的關(guān)注點(diǎn)分開(kāi)。集成測(cè)試可能需要方法間更緊密的協(xié)作,但一個(gè)單元測(cè)試不應(yīng)該對(duì)其他的單元測(cè)試有所依賴。而對(duì)于集成測(cè)試——也叫場(chǎng)景測(cè)試——的支持,也已在團(tuán)隊(duì)的計(jì)劃中。
  除了名字有所不同,這幾個(gè)注解與 JUnit 4 中的注解工作方式完全一樣。無(wú)獨(dú)有偶,跟主流意見(jiàn)一致,我也覺(jué)得這個(gè)新的命名不能說(shuō)服我其必要性。這個(gè) issue 下有更多的討論。
  禁用測(cè)試
  今兒星期五,抬頭一看已經(jīng)4點(diǎn)半,無(wú)心工作的你想回家了?完全理解,在測(cè)試上怒拍一個(gè) @Disabled 注解即可。有良心的話寫(xiě)個(gè)忽略測(cè)試的理由是極好的,不過(guò)也可以不帶此參數(shù)。
  @Test
  @Disabled("你丫是存心跑不過(guò)的是不?!")
  void failingTest() {
  assertTrue(false);
  }
  測(cè)試類的生命周期
  JUnit 團(tuán)隊(duì)發(fā)布的第一版原型中,包含了一個(gè)對(duì) 測(cè)試類的生命周期 的描述,有意思的是,這個(gè)特性在 alpha 版本的發(fā)布中未被加入。這個(gè)生命周期模型建議,在被測(cè)類的多個(gè)測(cè)試方法中使用一個(gè)同樣的實(shí)例,因?yàn)檫@樣我們可以通過(guò)改變對(duì)象的狀態(tài),進(jìn)而實(shí)現(xiàn)在多個(gè)測(cè)試方法中的交互。(我也再說(shuō)一遍,這更像是 場(chǎng)景測(cè)試 要管的事。)
  正如我在第一版公測(cè)時(shí)所說(shuō),這樣的特性99%的場(chǎng)景下是有害的,只有另外1%的場(chǎng)合下才有真正的用處。我只能說(shuō),還好這個(gè)特性被摒棄了。想想你的單元測(cè)試,如果它們必須靠在方法間維護(hù)狀態(tài)來(lái)工作,這畫(huà)面簡(jiǎn)直太美我不敢看?。
  斷言
  如果說(shuō) @Test、@Before...、@After... 等注解是一個(gè)測(cè)試套件的骨架,那么斷言是它的心臟。準(zhǔn)備好測(cè)試實(shí)例、執(zhí)行了被測(cè)類的方法以后,斷言能確保你得到了想要的結(jié)果。否則,說(shuō)明當(dāng)前測(cè)試失敗了。
  常規(guī)斷言
  一般的斷言,無(wú)非是檢查一個(gè)實(shí)例的屬性(比如,判空與判非空等),或者對(duì)兩個(gè)實(shí)例進(jìn)行比較(比如,檢查兩個(gè)實(shí)例對(duì)象是否相等)等。無(wú)論哪種檢查,斷言方法都可以接受一個(gè)字符串作為后一個(gè)可選參數(shù),它會(huì)在斷言失敗時(shí)提供必要的描述信息。如果提供出錯(cuò)信息的過(guò)程比較復(fù)雜,它也可以被包裝在一個(gè) lambda 表達(dá)式中,這樣,只有到真正失敗的時(shí)候,消息才會(huì)真正被構(gòu)造出來(lái)。
@Test
void assertWithBoolean() {
assertTrue(true);
assertTrue(this::truism);
assertFalse(false, () -> "Really " + "expensive " + "message" + ".");
}
boolean truism() {
return true;
}
@Test
void assertWithComparison() {
List<String> expected = asList("element");
List<String> actual = new LinkedList<>(expected);
assertEquals(expected, actual);
assertEquals(expected, actual, "Should be equal.");
assertEquals(expected, actual, () -> "Should " + "be " + "equal.");
assertNotSame(expected, actual, "Obviously not the same instance.");
}
  如你所見(jiàn),JUnit 5 的 API 并無(wú)太多變化。斷言方法的命名是一樣的,方法同樣接受兩個(gè)參數(shù),分別是一個(gè)期望值與一個(gè)實(shí)際值。
  期望值與實(shí)際值的傳入順序非常重要,無(wú)論是對(duì)于理解測(cè)試的內(nèi)容,還是理解失敗時(shí)的錯(cuò)誤信息,但有時(shí)還是很容易弄錯(cuò),這點(diǎn)很坑。不過(guò)仔細(xì)想想,也沒(méi)什么更好的辦法,除非你自己創(chuàng)建一個(gè)新的斷言框架。既然市面上已有對(duì)應(yīng)的產(chǎn)品如 Hamcrest (ugh!) 和AssertJ (yeah!譯者表示:不太清楚這歡呼的梗在哪里)等,再浪費(fèi)有限的時(shí)間去造輪子明顯不值得。畢竟重要的是保證你的斷言庫(kù)專注于一件事,借鑒已有實(shí)現(xiàn)可以節(jié)省成本。
  哦對(duì)了,失敗信息現(xiàn)在是作為后傳入的參數(shù)了。我很喜歡這個(gè)細(xì)節(jié),因?yàn)椋屇銓W⒂谡嬲匾?mdash;—那兩個(gè)需被斷言的值。由于擁抱了 Java 8 的緣故,真值斷言方法現(xiàn)在也接受 supplier 參數(shù)了,又是一個(gè)暖心的小細(xì)節(jié)。
  擴(kuò)展斷言
  除了那種一般的檢查特定實(shí)例或?qū)傩缘臄嘌酝,還有一些其他類型的斷言。
  這里要講的第一個(gè)甚至都不是個(gè)真正的斷言,它做的事是強(qiáng)行讓測(cè)試失敗,并提供一個(gè)失敗信息。
  @Test
  void failTheTest() {
  fail("epicly");
  }

上一頁(yè)12下一頁(yè)
軟件測(cè)試工具 | 聯(lián)系我們 | 投訴建議 | 誠(chéng)聘英才 | 申請(qǐng)使用列表 | 網(wǎng)站地圖
滬ICP備07036474 2003-2017 版權(quán)所有 上海澤眾軟件科技有限公司 Shanghai ZeZhong Software Co.,Ltd