使用 JUnit 對 System.in 進行單元測試
1. 概述
當測試依賴於控制台的用戶輸入的代碼時,該過程可能變得非常具有挑戰性。此外,測試用戶輸入場景是基於控制台或獨立應用程序的關鍵部分,因為我們需要確保正確處理不同的輸入。
在本教程中,我們將了解使用 JUnit 測試System.in
方法。
2. 理解System
類
在深入研究之前,讓我們先看一下System
類。它是java.lang
包中的最終類。
該類通過in
和out
變量提供對標準輸入和輸出流的訪問。與out
變量類似, System
類具有表示標準錯誤輸出流的err
變量。
此外,這些變量允許我們讀取和寫入控制台。使用這些流,我們允許用戶從控制台與我們的應用程序交互。
接下來, System.in
返回一個已經打開的InputStream
,可以從標準輸入讀取數據。使用System.i
n,我們可以將從鍵盤到 CPU 的輸入流重定向到我們的應用程序中。
3. 輸入示例
讓我們從本教程中將使用的簡單示例開始:
public static String readName() {
Scanner scanner = new Scanner(System.in);
String input = scanner.next();
return NAME.concat(input);
}
Java 提供了Scanner
類,它允許我們從各種來源讀取輸入,包括標準鍵盤輸入。此外,它提供了讀取用戶輸入的最簡單方法。使用Scanner
,我們可以讀取任何原始數據類型或String
。
在我們的示例方法中,我們使用next()
方法來讀取用戶的輸入。此外,該方法從輸入中讀取下一個單詞作為String
。
4. 使用核心Java
對標準輸入進行單元測試的第一種方法包括 Java API 提供的功能。
我們可以利用System.in
創建自定義InputStream
並在測試過程中模擬用戶輸入。
然而,在編寫單元測試之前,讓我們在測試類中編寫provideInput()
輔助方法:
void provideInput(String data) {
ByteArrayInputStream testIn = new ByteArrayInputStream(data.getBytes());
System.setIn(testIn);
}
在該方法內部,我們創建一個新的ByteArrayInputStream
並將所需的輸入作為byte
數組傳遞。
此外,我們使用System.setIn()
方法將自定義輸入流設置為System.in
的輸入。
現在,讓我們為示例方法編寫一個單元測試。我們可以調用應用程序類的readName()
方法,該方法現在讀取我們的自定義輸入:
@Test
void givenName_whenReadFromInput_thenReturnCorrectResult() {
provideInput("Baeldung");
String input = Application.readName();
assertEquals(NAME.concat("Baeldung"), input);
}
5. 使用系統規則庫和 JUnit 4
現在,讓我們看看如何使用系統規則庫和 JUnit 4 測試標準輸入。
首先,讓我們向pom.xml
添加所需的依賴項:
<dependency>
<groupId>com.github.stefanbirkner</groupId>
<artifactId>system-rules</artifactId>
<version>1.19.0</version>
<scope>test</scope>
</dependency>
該庫提供了用於測試依賴於System.in
和System.out
代碼的 JUnit 規則。
此外,它允許我們在測試執行期間重定向輸入和輸出流,這使得模擬用戶輸入變得很容易。
其次,為了測試System.in
,我們需要定義一個新的TextFromStandardInputStream
規則。我們將使用emptyStandardInputStream()
方法用空輸入流初始化變量:
@Rule
public final TextFromStandardInputStream systemIn = emptyStandardInputStream();
最後,我們來編寫單元測試:
@Test
public void givenName_whenReadWithSystemRules_thenReturnCorrectResult() {
systemIn.provideLines("Baeldung");
assertEquals(NAME.concat("Baeldung"), Application.readName());
}
此外,我們使用provideLines()
方法接受可變參數並將它們設置為輸入。
此外,測試執行後會恢復原來的System.in
。
6. 使用系統 Lambda 庫和 JUnit 5
值得一提的是,系統規則默認不支持 JUnit 5。但是,他們提供了一個系統 Lambda庫,我們可以將其與 JUnit 5 一起使用。
我們需要在pom.xml
中添加一個額外的依賴項:
<dependency>
<groupId>com.github.stefanbirkner</groupId>
<artifactId>system-lambda</artifactId>
<version>1.2.1</version>
<scope>test</scope>
</dependency>
現在,讓我們在測試中使用 System Lambda:
@Test
void givenName_whenReadWithSystemLambda_thenReturnCorrectResult() throws Exception {
withTextFromSystemIn("Baeldung")
.execute(() -> assertEquals(NAME.concat("Baeldung"), Application.readName()));
}
在這裡,我們使用SystemLambda
類中提供的withTextFromSystemIn()
靜態方法來設置System.in
中可用的輸入行。
7. 使用系統存根庫和 JUnit 4
此外,我們可以使用 JUnit 4 和系統存根庫測試標準輸入。
讓我們添加所需的依賴項:
<dependency>
<groupId>uk.org.webcompere</groupId>
<artifactId>system-stubs-junit4</artifactId>
<version>2.0.2</version>
<scope>test</scope>
</dependency>
接下來,讓我們創建SystemInRule
並傳遞所需的輸入值:
@Rule
public SystemInRule systemInRule = new SystemInRule("Baeldung");
現在,我們可以在單元測試中使用創建的規則:
@Test
public void givenName_whenReadWithSystemStubs_thenReturnCorrectResult() {
assertThat(Application.readName()).isEqualTo(NAME.concat("Baeldung"));
}
8. 使用系統存根庫和 JUnit 5
要使用 System Stubs 和 JUnit 5 測試System.in
,我們需要添加另一個依賴項:
<dependency>
<groupId>uk.org.webcompere</groupId>
<artifactId>system-stubs-jupiter</artifactId>
<version>2.0.2</version>
</dependency>
為了提供輸入值,我們將使用withTextFromSystemIn()
方法:
@Test
void givenName_whenReadWithSystemStubs_thenReturnCorrectResult() throws Exception {
SystemStubs.withTextFromSystemIn("Baeldung")
.execute(() -> {
assertThat(Application.readName())
.isEqualTo(NAME.concat("Baeldung"));
});
}
9. 結論
在本文中,我們學習瞭如何使用 JUnit 4 和 JUnit 5 測試System.in
。
通過第一種方法,我們學習瞭如何使用核心 Java 功能自定義System.in
。在第二種方法中,我們了解瞭如何使用系統規則庫。接下來,我們學習瞭如何使用 System Lambda 庫通過 JUnit 5 編寫測試。最後,我們了解瞭如何使用 System Stubs 庫。
與往常一樣,本文的完整源代碼可以在 GitHub 上獲取。