本教程介紹 JUnit 5。我們首先介紹如何在您的計算機上安裝並設置 JUnit 5。我將簡要介紹 JUnit 5 的架構和組件,然后展示如何使用 JUnit Jupiter API 中的新注解、斷言和前置條件。
在第 2 部分中,我們將更深入地介紹 JUnit 5,包括新的 JUnit Jupiter 擴展模型、參數注入、動態測試等。
在本教程中,我使用了 JUnit 5, Version 5.0.2。
前提條件
出於本教程的目的,我假設您熟悉以下軟件的使用:
- Eclipse IDE
- Maven
- Gradle(可選)
- Git
要跟隨示例進行操作,您應在計算機上安裝 JDK 8、Eclipse、Maven、Gradle(可選)和 Git。如果缺少其中的任何工具,可使用下面的鏈接下載和安裝它們:
- JDK 8 for Windows, Mac, and Linux。
- Eclipse IDE for Windows, Mac, and Linux。
- Apache Maven for Windows, Mac, and Linux。
- Gradle for Windows, Mac, and Linux。
- Git for Windows, Mac, and Linux。
術語
人們傾向於將術語 JUnit 5 和 JUnit Jupiter 當作同義詞使用。在大部分情況下,這種互換使用沒有什么問題。但是,一定要認識到這兩個術語是不同的。JUnit Jupiter 是使用 JUnit 5 編寫測試內容的 API。JUnit 5 是一個項目名稱(和版本),其 3 個主要模塊關注不同的方面:JUnit Jupiter、JUnit Platform 和 JUnit Vintage。
當我提及 JUnit Jupiter 時,指的是編寫單元測試的 API;提及 JUnit 5 時,指的是整個項目。
JUnit 5 概述
以前的 JUnit 版本都是整體式的。除了在 4.4 版中包含 Hamcrest JAR,JUnit 基本來講就是一個很大的 JAR 文件。測試內容編寫者 — 像您我這樣的開發人員 — 和工具供應商都使用它的 API,但后者使用很多內部 JUnit API。
大量使用內部 API 給 JUnit 的維護者造成了一些麻煩,並且留給他們推動該技術發展的選擇余地不多。來自 JUnit 5 用戶指南:
“在 JUnit 4 中,只有外部擴展編寫者和工具構建者才使用最初作為內部結構而添加的許多功能。這讓更改 JUnit 4 變得特別困難,有時甚至根本不可能。”
JUnit Lambda(現在稱為 JUnit 5)團隊決定將 JUnit 重新設計為兩個明確且不同的關注區域:
- 一個是編寫測試內容的 API。
- 一個是發現和運行這些測試的 API。
這些關注區域現在已整合到 JUnit 5 的架構中,並且它們是明確分離的。圖 1 演示了新架構(圖像來自 Nicolai Parlog):
圖 1. JUnit 5 的架構
如果仔細查看圖 1,就會發現 JUnit 5 的架構有多么強大。好了,讓我們仔細看看這個架構。右上角的方框表明,對 JUnit 5 而言,JUnit Jupiter API 只是另一個 API!因為 JUnit Jupiter 的組件遵循新的架構,所以它們可應用 JUnit 5,但您可以輕松定義不同的測試框架。只要一個框架實現了 TestEngine
接口,就可以將它插入任何支持 junit-platform-engine
和 junit-platform-launcher
API 的工具中!
我仍然認為 JUnit Jupiter 非常特殊(畢竟我即將用一整篇教程來介紹它),但 JUnit 5 團隊完成的工作確實具有開創性。我只是想指出這一點。我們繼續看看圖 1,直到我們完全達成一致。
使用 JUnit Jupiter 編寫測試內容
就測試編寫者而言,任何符合 JUnit 規范的測試框架(包括 JUnit Jupiter)都包含兩個組件:
- 我們為其編寫測試的 API。
- 理解這個特定 API 的 JUnit
TestEngine
實現。
對於本教程,前者是 JUnit Jupiter API,后者是 JUnit Jupiter Test Engine。我將介紹這二者。
JUnit Jupiter API
作為開發人員,您將使用 JUnit Jupiter API 創建單元測試來測試您的應用程序代碼。使用該 API 的基本特性 — 注解、斷言等 — 是本部分教程的主要關注點。
JUnit Jupiter API 的設計讓您可通過插入各種生命周期回調來擴展它的功能。您將在第 2 部分中了解如何使用這些回調完成有趣的工作,比如運行參數化測試,將參數傳遞給測試方法,等等。
JUnit Jupiter Test Engine
您將使用 JUnit Jupiter Test Engine 發現和執行 JUnit Jupiter 單元測試。該測試引擎實現了 JUnit Platform 中包含的 TestEngine
接口。可將 TestEngine
看作單元測試與用於啟動它們的工具(比如 IDE)之間的橋梁。
使用 JUnit Platform 運行測試
在 JUnit 術語中,運行單元測試的過程分為兩部分:
- 發現測試和創建測試計划。
- 啟動測試計划,以 (1) 執行測試和 (2) 向用戶報告結果。
用於發現測試的 API
用於發現測試和創建測試計划的 API 包含在 JUnit Platform 中,由一個 TestEngine
實現。該測試框架將測試發現功能封裝到其 TestEngine
實現中。JUnit Platform 負責使用 IDE 和構建工具(比如 Gradle 和 Maven)發起測試發現流程。
測試發現的目的是創建測試計划,該計划中包含一個測試規范。測試規范包含以下組件:
- 選擇器,比如:
- 要掃描哪個包來尋找測試類
- 特定的類名稱
- 特定的方法
- 類路徑根文件夾
- 過濾器,比如:
- 類名稱模式(比如 “.*Test”)
- 標簽(將在第 2 部分中討論)
- 特定的測試引擎(比如 “junit-jupiter”)
測試計划是根據測試規范所發現的所有測試類、這些類中的測試方法、測試引擎等的分層視圖。測試計划准備就緒后,就可以執行了。
用於執行測試的 API
用於執行測試的 API 包含在 JUnit Platform 中,由一個或多個 TestEngine
實現。測試框架將測試執行功能封裝在它們的 TestEngine
實現中,但 JUnit Platform 負責發起測試執行流程。通過 IDE 和構建工具(比如 Gradle 和 Maven)發起測試執行工作。
一個名為 Launcher
的 JUnit Platform 組件負責執行在測試發現期間創建的測試計划。某個流程 — 假設是您的 IDE — 通過 JUnit Platform(具體來講是 junit-platform-launcher
API)發起測試執行流程。這時,JUnit Platform 將測試計划連同 TestExecutionListener
一起傳遞給 Launcher
。TestExecutionListener
將報告測試執行結果,從而在您的 IDE 中顯示該結果。
測試執行流程的目的是向用戶准確報告在測試運行時發生了哪些事件。這包括測試成功和失敗報告,以及伴隨失敗而生成的消息,幫助用戶理解所發生的事件。
后向兼容性:JUnit Vintage
許多組織對 JUnit 3 和 4 進行了大力投資,因此無法承擔向 JUnit 5 的大規模轉換。了解到這一點后,JUnit 5 團隊提供了 junit-vintage-engine
和 junit-jupiter-migration-support
組件來幫助企業進行遷移。
對 JUnit Platform 而言,JUnit Vintage 只是另一個測試框架,包含自己的 TestEngine
和 API(具體來講是 JUnit 4 API)。
圖 2 顯示了各種 JUnit 5 包之間的依賴關系。
圖 2. JUnit 5 包關系圖
opentest4j 的用途
支持 JUnit 的測試框架在如何處理測試執行期間拋出的異常方面有所不同。JVM 上的測試沒有統一標准,這是 JUnit 團隊一直要面對的問題。除了 java.lang.AssertionError
,測試框架還必須定義自己的異常分層結構,或者將自身與 JUnit 支持的異常結合起來(或者在某些情況下同時采取兩種方法)。
為了解決一致性問題,JUnit 團隊提議建立一個開源項目,該項目目前稱為 Open Test Alliance for the JVM(JVM 開放測試聯盟)。該聯盟在此階段僅是一個提案,它僅定義了初步的異常分層結構。但是,JUnit 5 使用 opentest4j
異常。(可在圖 2 中看到這一點;請注意從 junit-jupiter-api
和 junit-platform-engine
包到 opentest4j
包的依賴線。)
現在您已基本了解各種 JUnit 5 組件如何結合在一起,是時候使用 JUnit Jupiter API 編寫一些測試了!
使用 JUnit Jupiter 編寫測試
注解
從 JUnit 4 開始,注解 (annotation) 就成為測試框架的核心特性,這一趨勢在 JUnit 5 中得以延續。我無法介紹 JUnit 5 的所有注解,本節僅簡要介紹最常用的注解。
首先,我將比較 JUnit 4 中與 JUnit 5 中的注解。JUnit 5 團隊更改了一些注解的名稱,讓它們更直觀,同時保持功能不變。如果您正在使用 JUnit 4,下表將幫助您適應這些更改。
表 1. JUnit 4 與 JUnit 5 中的注解比較
使用注解
接下來看看一些使用這些注解的示例。盡管一些注解已在 JUnit 5 中重命名,但如果您使用過 JUnit 4,應熟悉它們的功能。清單 1 中的代碼來自 JUnit5AppTest.java
,可在 HelloJUnit5 示例應用程序中找到。
清單 1. 基本注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
@RunWith(JUnitPlatform.class)
@DisplayName("Testing using JUnit 5")
public class JUnit5AppTest {
private static final Logger log = LoggerFactory.getLogger(JUnit5AppTest.class);
private App classUnderTest;
@BeforeAll
public static void init() {
// Do something before ANY test is run in this class
}
@AfterAll
public static void done() {
// Do something after ALL tests in this class are run
}
@BeforeEach
public void setUp() throws Exception {
classUnderTest = new App();
}
@AfterEach
public void tearDown() throws Exception {
classUnderTest = null;
}
@Test
@DisplayName("Dummy test")
void aTest() {
log.info("As written, this test will always pass!");
assertEquals(4, (2 + 2));
}
@Test
@Disabled
@DisplayName("A disabled test")
void testNotRun() {
log.info("This test will not run (it is disabled, silly).");
}
.
.
}
|
看看上面突出顯示行中的注解:
- 第 1 行:
@RunWith
連同它的參數JUnitPlatform.class
(一個基於 JUnit 4 且理解 JUnit Platform 的Runner
)讓您可以在 Eclipse 內運行 JUnit Jupiter 單元測試。Eclipse 尚未原生支持 JUnit 5。未來,Eclipse 將提供原生的 JUnit 5 支持,那時我們不再需要此注解。 - 第 2 行:
@DisplayName
告訴 JUnit 在報告測試結果時顯示String
“Testing using JUnit 5”,而不是測試類的名稱。 - 第 9 行:
@BeforeAll
告訴 JUnit 在運行這個類中的所有@Test
方法之前運行init()
方法一次。 - 第 14 行:
@AfterAll
告訴 JUnit 在運行這個類中的所有@Test
方法之后運行done()
方法一次。 - 第 19 行:
@BeforeEach
告訴 JUnit 在此類中的每個@Test
方法之前運行setUp()
方法。 - 第 24 行:
@AfterEach
告訴 JUnit 在此類中的每個@Test
方法之后運行tearDown()
方法。 - 第 29 行:
@Test
告訴 JUnit,aTest()
方法是一個 JUnit Jupiter 測試方法。 - 第 37 行:
@Disabled
告訴 JUnit 不運行此@Test
方法,因為它已被禁用。
斷言
斷言 (assertion) 是 org.junit.jupiter.api.Assertions
類上的眾多靜態方法之一。斷言用於測試一個條件,該條件必須計算為 true
,測試才能繼續執行。
如果斷言失敗,測試會在斷言所在的代碼行上停止,並生成斷言失敗報告。如果斷言成功,測試會繼續執行下一行代碼。
表 2 中列出的所有 JUnit Jupiter 斷言方法都接受一個可選的 message
參數(作為最后一個參數),以顯示斷言是否失敗,而不是顯示標准的缺省消息。
表 2. JUnit Jupiter 中的斷言
清單 2 給出了一個使用這些斷言的示例,該示例來自 HelloJUnit5 示例應用程序。
清單 2. 示例應用程序中的 JUnit Jupiter 斷言
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
.
.
@Test
@DisplayName("Dummy test")
void dummyTest() {
int expected = 4;
int actual = 2 + 2;
assertEquals(expected, actual, "INCONCEIVABLE!");
//
Object nullValue = null;
assertFalse(nullValue != null);
assertNull(nullValue);
assertNotNull("A String", "INCONCEIVABLE!");
assertTrue(nullValue == null);
.
.
}
|
看看上面突出顯示行中的斷言:
- 第 13 行:
assertEquals
:如果第一個參數值 (4) 不等於第二個參數值 (2+2),則斷言失敗。在報告斷言失敗時使用用戶提供的消息(該方法的第 3 個參數)。 - 第 16 行:
assertFalse
:表達式nullValue != null
必須為false
,否則斷言失敗。 - 第 17 行:
assertNull
:nullValue
參數必須為null
,否則斷言失敗。 - 第 18 行:
assertNotNull
:String
文字值 “A String” 不得為null
,否則斷言失敗並報告消息 “INCONCEIVABLE!”(而不是缺省的 “Assertion failed” 消息)。 - 第 19 行:
assertTrue
:如果表達式nullValue == null
不等於true
,則斷言失敗。
除了支持這些標准斷言,JUnit Jupiter AP 還提供了多個新斷言。下面介紹其中的兩個。
方法 @assertAll()
清單 3 中的 @assertAll()
方法給出了清單 2 中看到的相同斷言,但包裝在一個新的斷言方法中:
清單 3. assertAll()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import static org.junit.jupiter.api.Assertions.assertAll;
.
.
@Test
@DisplayName("Dummy test")
void dummyTest() {
int expected = 4;
int actual = 2 + 2;
Object nullValue = null;
.
.
assertAll(
"Assert All of these",
() -> assertEquals(expected, actual, "INCONCEIVABLE!"),
() -> assertFalse(nullValue != null),
() -> assertNull(nullValue),
() -> assertNotNull("A String", "INCONCEIVABLE!"),
() -> assertTrue(nullValue == null));
}
|
assertAll()
的有趣之處在於,它包含的所有斷言都會執行,即使一個或多個斷言失敗也是如此。與此相反,在清單 2 中的代碼中,如果任何斷言失敗,測試就會在該位置失敗,意味着不會執行任何其他斷言。
方法 @assertThrows()
在某些條件下,接受測試的類應拋出異常。JUnit 4 通過 expected =
方法參數或一個 @Rule
提供此能力。與此相反,JUnit Jupiter 通過 Assertions
類提供此能力,使它與其他斷言更加一致。
我們將所預期的異常視為可以進行斷言的另一個條件,因此 Assertions
包含處理此條件的方法。清單 4 引入了新的 assertThrows()
斷言方法。
清單 4. assertThrows()
1
2
3
4
5
6
7
8
9
10
|
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertEquals;
.
.
@Test()
@DisplayName("Empty argument")
public void testAdd_ZeroOperands_EmptyArgument() {
long[] numbersToSum = {};
assertThrows(IllegalArgumentException.class, () -> classUnderTest.add(numbersToSum));
}
|
請注意第 9 行:如果對 classUnderTest.add()
的調用沒有拋出 IllegalArgumentException
,則斷言失敗。
前置條件
前置條件 (Assumption) 與斷言類似,但前置條件必須為 true,否則測試將中止。與此相反,當斷言失敗時,則將測試視為已失敗。測試方法只應在某些條件 —前置條件下執行時,前置條件很有用。
前置條件是 org.junit.jupiter.api.Assumptions
類的靜態方法。要理解前置條件的價值,只需一個簡單的示例。
假如您只想在星期五運行一個特定的單元測試(我假設您有自己的理由):
1
2
3
4
5
6
7
|
@Test
@DisplayName("This test is only run on Fridays")
public void testAdd_OnlyOnFriday() {
LocalDateTime ldt = LocalDateTime.now();
assumeTrue(ldt.getDayOfWeek().getValue() == 5);
// Remainder of test (only executed if assumption holds)...
}
|
在此情況下,如果條件不成立(第 5 行),就不會執行 lambda 表達式的內容。
請注意第 5 行:如果該條件不成立,則跳過該測試。在此情況下,該測試不是在星期五 (5) 運行的。這不會影響項目的 “綠色” 部分,而且不會導致構建失敗;會跳過 assumeTrue()
后的測試方法中的所有代碼。
如果在前置條件成立時僅應執行測試方法的一部分,可以使用 assumingThat()
方法編寫上述條件,該方法使用 lambda 語法:
1
2
3
4
5
6
7
8
9
10
|
@Test
@DisplayName("This test is only run on Fridays (with lambda)")
public void testAdd_OnlyOnFriday_WithLambda() {
LocalDateTime ldt = LocalDateTime.now();
assumingThat(ldt.getDayOfWeek().getValue() == 5,
() -> {
// Execute this if assumption holds...
});
// Execute this regardless
}
|
注意,無論 assumingThat()
中的前置條件成立與否,都會執行 lambda 表達式后的所有代碼。
嵌套單元測試,實現清晰的結構
在繼續介紹下節內容之前,我想介紹在 JUnit 5 中編寫單元測試的最后一個特性。
JUnit Jupiter API 允許您創建嵌套的類,以保持測試代碼更清晰,這有助於讓測試結果更易讀。通過在主類中創建嵌套的測試類,可以創建更多的名稱空間,這提供了兩個主要優勢:
- 每個單元測試可以擁有自己的測試前和測試后生命周期。這讓您能使用特殊條件創建要測試的類,從而測試極端情況。
- 單元測試方法的名稱變得更簡單。在 JUnit 4 中,所有測試方法都以對等形式存在,不允許重復的方法名(所以您最終會得到類似
testMethodButOnlyUnderThisOrThatCondition_2()
的方法名)。從 JUnit Jupiter 開始,只有嵌套類中的方法必須具有唯一的名稱。清單 6 展示了這一優勢。
清單 5. 傳遞一個空或 null 數組引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@RunWith(JUnitPlatform.class)
@DisplayName("Testing JUnit 5")
public class JUnit5AppTest {
.
.
@Nested
@DisplayName("When zero operands")
class JUnit5AppZeroOperandsTest {
// @Test methods go here...
}
.
.
}
|
請注意第 6 行,其中的 JUnit5AppZeroOperandsTest
類可以擁有測試方法。任何測試的結果都會在父類 JUnit5AppTest
中以嵌套的形式顯示。
使用 JUnit Platform 運行測試
能編寫單元測試很不錯,但如果不能運行它們,就沒有什么意義了。本節展示如何在 Eclipse 中運行 JUnit 測試,首先使用 Maven,然后從命令行使用 Gradle。
下面的視頻展示了如何從 GitHub 克隆示例應用程序代碼,並在 Eclipse 中運行測試。在該視頻中,我還展示了如何從命令行以及 Eclipse 內使用 Maven 和 Gradle 運行單元測試。Eclipse 對 Maven 和 Gradle 都提供了很好的支持。
應用 3 種工具運行單元測試
下面將提供一些簡要的說明,但該視頻提供了更多細節。觀看該視頻,了解如何:
- 從 GitHub 克隆 HelloJUnit5 示例應用程序。
- 將應用程序導入 Eclipse 中。
- 從 Eclipse 內的 HelloJUnit5 應用程序運行一個 JUnit 測試。
- 使用 Maven 從命令行運行 HelloJUnit5 單元測試。
- 使用 Gradle 從命令行運行 HelloJUnit5 單元測試。
克隆 HelloJUnit5 示例應用程序
要理解教程的剩余部分,您需要從 GitHub 克隆示例應用程序。為此,可打開一個終端窗口 (Mac) 或命令提示 (Windows),導航到您希望放入代碼的目錄,然后輸入以下命令:
git clone https://github.com/makotogo/HelloJUnit5
|
現在您的機器上已擁有該代碼,可以在 Eclipse IDE 內運行 JUnit 測試了。接下來介紹如何運行測試。
在 Eclipse IDE 中運行單元測試
如果您已跟隨該視頻進行操作,應該已將代碼導入 Eclipse 中。現在,在 Eclipse 中打開 Project Explorer 視圖,展開 HelloJUnit5 項目,直至看到 src/test/java
路徑下的 JUnit5AppTest
類。
打開 JUnit5AppTest.java
並驗證 class
定義前的下面這個注解(以下代碼的第 3 行):
1
2
3
4
5
6
7
|
.
.
@RunWith(JUnitPlatform.class)
public class JUnit5AppTest {
.
.
}
|
現在右鍵單擊 JUnit5AppTest
並選擇 Run As > JUnit Test。單元測試運行時,JUnit 視圖將會出現。您現在已准備好完成本教程的練習。
使用 Maven 運行單元測試
打開一個終端窗口 (Mac) 或命令提示 (Windows),導航到您將 HelloJUnit5 應用程序克隆到的目錄,然后輸入以下命令:
mvn test
|
這會啟動 Maven 構建並運行單元測試。您的輸出應類似於:
$ mvn clean test
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building HelloJUnit5 1.0.2
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ HelloJUnit5 ---
[INFO] Deleting /Users/sperry/home/development/projects/learn/HelloJUnit5/target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ HelloJUnit5 ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/sperry/home/development/projects/learn/HelloJUnit5/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.6.1:compile (default-compile) @ HelloJUnit5 ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to /Users/sperry/home/development/projects/learn/HelloJUnit5/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ HelloJUnit5 ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/sperry/home/development/projects/learn/HelloJUnit5/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.6.1:testCompile (default-testCompile) @ HelloJUnit5 ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to /Users/sperry/home/development/projects/learn/HelloJUnit5/target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.19:test (default-test) @ HelloJUnit5 ---
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Nov 28, 2017 6:04:49 PM org.junit.vintage.engine.discovery.DefensiveAllDefaultPossibilitiesBuilder$DefensiveAnnotatedBuilder buildRunner
WARNING: Ignoring test class using JUnitPlatform runner: com.makotojava.learn.hellojunit5.solution.JUnit5AppTest
Running com.makotojava.learn.hellojunit5.solution.JUnit5AppTest
Nov 28, 2017 6:04:49 PM org.junit.vintage.engine.discovery.DefensiveAllDefaultPossibilitiesBuilder$DefensiveAnnotatedBuilder buildRunner
WARNING: Ignoring test class using JUnitPlatform runner: com.makotojava.learn.hellojunit5.solution.JUnit5AppTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 1, Time elapsed: 0.038 sec - in com.makotojava.learn.hellojunit5.solution.JUnit5AppTest
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 1
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.741 s
[INFO] Finished at: 2017-11-28T18:04:50-06:00
[INFO] Final Memory: 21M/255M
[INFO] ------------------------------------------------------------------------
|
Running unit tests with Gradle
Open a terminal window (Mac) or command prompt (Windows), navigate to the directory where you cloned the HelloJUnit5 application, and enter this command:
gradle clean test
|
The output should look like this:
$ gradle clean test
Starting a Gradle Daemon (subsequent builds will be faster)
:clean
:compileJava
:processResources NO-SOURCE
:classes
:compileTestJava
:processTestResources NO-SOURCE
:testClasses
:junitPlatformTest
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.
Test run finished after 10097 ms
[ 7 containers found ]
[ 5 containers skipped ]
[ 2 containers started ]
[ 0 containers aborted ]
[ 2 containers successful ]
[ 0 containers failed ]
[ 10 tests found ]
[ 10 tests skipped ]
[ 0 tests started ]
[ 0 tests aborted ]
[ 0 tests successful ]
[ 0 tests failed ]
:test SKIPPED
BUILD SUCCESSFUL
Total time: 21.014 secs
|
測試練習
現在您已了解 JUnit Jupiter,查看了代碼示例,並觀看了視頻(希望您已跟隨視頻進行操作)。非常棒,但沒有什么比動手編寫代碼更有用了!在第 1 部分的最后一節,您將完成以下任務:
- 編寫 JUnit Jupiter API 單元測試。
- 運行單元測試。
- 實現
App
類,讓您的單元測試通過檢查。
采用真正的測試驅動開發 (TDD) 方式,首先編寫單元測試,運行它們,並會觀察到它們全部失敗了。然后編寫實現,直到單元測試通過,這時您就大功告成了。
注意,JUnit5AppTest
類僅提供了兩個現成的測試方法。首次運行該類時,二者都是 “綠色” 的。要完成這些練習,您需要添加剩余的代碼,包括用於告訴 JUnit 運行哪些測試方法的注解。記住,如果沒有正確配備一個類或方法,JUnit 將跳過它。
如果遇到困難,請查閱 com.makotojava.learn.hellojunit5.solution
包來尋找解決方案。
編寫 JUnit Jupiter 單元測試
首先從 JUnit5AppTest.java
開始。打開此文件並按照 Javadoc 注解中的指示操作。
提示:使用 Eclipse 中的 Javadoc 視圖讀取測試指令。要打開 Javadoc 視圖,可以轉到 Window > Show View > Javadoc。您應該看到 Javadoc 視圖。根據您設置工作區的方式,該窗口可能出現在任意多個位置。在我的工作區中,該窗口與圖 3 中的屏幕截圖類似,出現在 IDE 右側的編輯器窗口下方:
圖 3. Javadoc 視圖
編輯器窗口中顯示了具有原始 HTML 標記的 Javadoc 注解,但在 Javadoc 窗口中,已將其格式化,因此更易於閱讀。
在 Eclipse 中運行單元測試
如果您像我一樣,您會使用 IDE 執行以下工作:
- 編寫單元測試。
- 編寫單元測試所測試的實現內容。
- 運行初始測試(使用 IDE 的原生 JUnit 支持)。
JUnit 5 提供了一個名為 JUnitPlatform
的類,它允許您在 Eclipse 中運行 JUnit 5 測試。
要在 Eclipse 中運行測試,需要確保您的計算機上擁有示例應用程序。為此,最輕松的方法是從 GitHub 克隆 HelloJUnit5 應用程序,然后將它導入 Eclipse 中。(因為本教程的視頻展示了如何這么做,所以這里將跳過細節,僅提供操作步驟。)
確保您克隆了 GitHub 存儲庫,然后將代碼導入 Eclipse 中作為新的 Maven 項目。
將該項目導入 Eclipse 中后,打開 Project Explorer 視圖並展開 src/main/test
節點,直至看到 JUnit5AppTest
。要以 JUnit 測試的形式運行它,可以右鍵單擊它,選擇 Run As > JUnit Test。
實現 App 類,直到單元測試通過檢查
App
的單一 add()
方法提供的功能很容易理解,而且在設計上非常簡單。我不希望復雜應用程序的業務邏輯阻礙您對 JUnit Jupiter 的學習。
單元測試通過后,您就大功告成了!記住,如果遇到困難,可以在 com.makotojava.learn.hellojunit5.solution
包中查找解決方案。
第 1 部分小結
在 JUnit 5 教程的前半部分中,我介紹了 JUnit 5 的架構和組件,並詳細介紹了 JUnit Jupiter API。我們逐個介紹了 JUnit 5 中最常用的注解、斷言和前置條件,而且通過一個快速練習演示了如何在 Eclipse、Maven 和 Gradle 中運行測試。
在第 2 部分中,您將了解 JUnit 5 的一些高級特性:
- JUnit Jupiter 擴展模型
- 方法參數注入
- 參數化測試
那么您接下來會怎么做?