攝影作品網站源碼軟文寫作范文
JUnit 5參數化測試
目錄
- 設置
- 我們的第一個參數化測試
- 參數來源
- @ValueSource
- @NullSource & @EmptySource
- @MethodSource
- @CsvSource
- @CsvFileSource
- @EnumSource
- @ArgumentsSource
- 參數轉換
- 參數聚合
- 獎勵
- 總結
如果您正在閱讀這篇文章,說明您已經熟悉了JUnit。讓我為您概括一下JUnit——在軟件開發(fā)中,我們開發(fā)人員編寫的代碼可能是設計一個人的個人資料這樣簡單,也可能是在銀行系統(tǒng)中進行付款這樣復雜。在開發(fā)這些功能時,我們傾向于編寫單元測試。顧名思義,單元測試的主要目的是確保代碼的小、單獨部分按預期功能工作。
如果單元測試執(zhí)行失敗,這意味著該功能無法按預期工作。編寫單元測試的一種工具是JUnit。這些單元測試程序很小,但是非常強大,并且可以快速執(zhí)行?,F在我們已經了解了JUnit,接下來讓我們聚焦于JUnit 5中的參數化測試。
參數化測試可以解決在為任何新/舊功能開發(fā)測試框架時遇到的最常見問題。
- 編寫針對每個可能輸入的測試用例變得更加容易。
- 單個測試用例可以接受多個輸入來測試源代碼,有助于減少代碼重復。
- 通過使用多個輸入運行單個測試用例,我們可以確信已涵蓋所有可能的場景,并維護更好的代碼覆蓋率。
開發(fā)團隊通過利用方法和類來創(chuàng)建可重用且松散耦合的源代碼。傳遞給代碼的參數會影響其功能。例如,計算器類中的sum方法可以處理整數和浮點數值。JUnit 5引入了執(zhí)行參數化測試的能力,可以使用單個測試用例測試源代碼,該測試用例可以接受不同的輸入。這樣可以更有效地進行測試,因為在舊版本的JUnit中,必須為每種輸入類型創(chuàng)建單獨的測試用例,從而導致大量的代碼重復。
?示例代碼
本文附帶有在?GitHub上(
code-examples/core-java/junit5-parameterized-tests at master · thombergs/code-examples · GitHub) 的一個可工作的示例代碼。
設置
就像瘋狂泰坦滅霸喜歡訪問力量一樣,您可以使用以下Maven依賴項來訪問JUnit5中參數化測試的力量:
<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-params</artifactId><version>5.9.2</version><scope>test</scope>
</dependency>
讓我們來寫些代碼,好嗎?
我們的第一個參數化測試
現在,我想向您介紹一個新的注解 @ParameterizedTest。顧名思義,它告訴JUnit引擎使用不同的輸入值運行此測試。
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;public class ValueSourceTest {@ParameterizedTest@ValueSource(ints = { 2, 4 })void checkEvenNumber(int number) {assertEquals(0, number % 2,"Supplied number is not an even number");}
}
在上面的示例中,注解@ValueSource為 checkEvenNumber() 方法提供了多個輸入。假設我們使用JUnit4編寫相同的代碼,即使它們的結果(斷言)完全相同,我們也必須編寫2個測試用例來覆蓋輸入2和4。
當我們執(zhí)行 ValueSourceTest 時,我們會看到什么:
ValueSourceTest
|_ checkEvenNumber
|_ [1] 2
|_ [2] 4
這意味著 checkEvenNumber() 方法將使用2個輸入值執(zhí)行。
在下一節(jié)中,讓我們學習一下JUnit5框架提供的各種參數來源。
現在我也找了很多測試的朋友,做了一個分享技術的交流群,共享了很多我們收集的技術文檔和視頻教程。
如果你不想再體驗自學時找不到資源,沒人解答問題,堅持幾天便放棄的感受
可以加入我們一起交流。而且還有很多在自動化,性能,安全,測試開發(fā)等等方面有一定建樹的技術大牛
分享他們的經驗,還會分享很多直播講座和技術沙龍
可以免費學習!劃重點!開源的!!!
qq群號:110685036
參數來源
JUnit5提供了許多參數來源注釋。下面的章節(jié)將簡要概述其中一些注釋并提供示例。
@ValueSource
@ValueSource是一個簡單的參數源,可以接受單個字面值數組。@ValueSource支持的字面值類型有short、byte、int、long、float、double、char、boolean、String和Class。
@ParameterizedTest
@ValueSource(strings = { "a1", "b2" })
void checkAlphanumeric(String word) {assertTrue(StringUtils.isAlphanumeric(word),"Supplied word is not alpha-numeric");
}
@NullSource & @EmptySource
假設我們需要驗證用戶是否已經提供了所有必填字段(例如在登錄函數中需要提供用戶名和密碼)。我們使用注解來檢查提供的字段是否為 null,空字符串或空格。
- 在單元測試中使用 @NullSource 和 @EmptySource 可以幫助我們提供帶有 null、空字符串和空格的數據源,并驗證源代碼的行為。
@ParameterizedTest
@NullSource
void checkNull(String value) {assertEquals(null, value);
}@ParameterizedTest
@EmptySource
void checkEmpty(String value) {assertEquals("", value);
}
- 我們還可以使用 @NullAndEmptySource 注解來組合傳遞 null 和空輸入。
@ParameterizedTest
@NullAndEmptySource
void checkNullAndEmpty(String value) {assertTrue(value == null || value.isEmpty());
}
- 另一個傳遞 null、空字符串和空格輸入值的技巧是結合使用 @NullAndEmptySource 注解,以覆蓋所有可能的負面情況。該注解允許我們從一個或多個測試類的工廠方法中加載輸入,并生成一個參數流。
@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = { " ", " " })
void checkNullEmptyAndBlank(String value) {assertTrue(value == null || value.isBlank());
}
@MethodSource
該注解允許我們從一個或多個測試類的工廠方法中加載輸入,并生成一個參數流。
- 顯式方法源?- 測試將嘗試加載提供的方法。
// Note: The test will try to load the supplied method
@ParameterizedTest
@MethodSource("checkExplicitMethodSourceArgs")
void checkExplicitMethodSource(String word) {
assertTrue(StringUtils.isAlphanumeric(word),
"Supplied word is not alpha-numeric");
}static Stream<String> checkExplicitMethodSourceArgs() {
return Stream.of("a1",
"b2");
}
- 隱式方法源?- 測試將搜索與測試類匹配的源方法。
// Note: The test will search for the source method
// that matches the test-case method name
@ParameterizedTest
@MethodSource
void checkImplicitMethodSource(String word) {assertTrue(StringUtils.isAlphanumeric(word),
"Supplied word is not alpha-numeric");
}static Stream<String> checkImplicitMethodSource() {
return Stream.of("a1",
"b2");
}
- 多參數方法源?- 我們必須將輸入作為參數流傳遞。測試將按照索引順序加載參數。
// Note: The test will automatically map arguments based on the index
@ParameterizedTest
@MethodSource
void checkMultiArgumentsMethodSource(int number, String expected) {assertEquals(StringUtils.equals(expected, "even") ? 0 : 1, number % 2);
}static Stream<Arguments> checkMultiArgumentsMethodSource() {return Stream.of(Arguments.of(2, "even"),Arguments.of(3, "odd"));
}
- 外部方法源?- 測試將嘗試加載外部方法。
// Note: The test will try to load the external method
@ParameterizedTest
@MethodSource(
"source.method.ExternalMethodSource#checkExternalMethodSourceArgs")
void checkExternalMethodSource(String word) {assertTrue(StringUtils.isAlphanumeric(word),
"Supplied word is not alpha-numeric");
}
// Note: The test will try to load the external method@ParameterizedTest@MethodSource("source.method.ExternalMethodSource#checkExternalMethodSourceArgs")void checkExternalMethodSource(String word) { assertTrue(StringUtils.isAlphanumeric(word),"Supplied word is not alpha-numeric");}package source.method;
import java.util.stream.Stream;public class ExternalMethodSource {static Stream<String> checkExternalMethodSourceArgs() {return Stream.of("a1","b2");}
}
@CsvSource
該注解允許我們將參數列表作為逗號分隔的值(即 CSV 字符串字面量)傳遞,每個 CSV 記錄都會導致執(zhí)行一次參數化測試。它還支持使用 useHeadersInDisplayName屬性跳過 CSV 標頭。
@ParameterizedTest
@CsvSource({ "2, even",
"3, odd"})
void checkCsvSource(int number, String expected) {assertEquals(StringUtils.equals(expected, "even")? 0 : 1, number % 2);
}
@CsvFileSource
該注解允許我們使用類路徑或本地文件系統(tǒng)中的逗號分隔值(CSV)文件。與 @CsvSource 類似,每個 CSV 記錄都會導致執(zhí)行一次參數化測試。它還支持各種其他屬性 -numLinesToSkip、useHeadersInDisplayName、lineSeparator、delimiterString等。
示例 1: 基本實現
@ParameterizedTest
@CsvFileSource(
files = "src/test/resources/csv-file-source.csv",
numLinesToSkip = 1)
void checkCsvFileSource(int number, String expected) {assertEquals(StringUtils.equals(expected, "even")? 0 : 1, number % 2);
}
src/test/resources/csv-file-source.csv
NUMBER, ODD_EVEN
2, even
3, odd
示例2:使用屬性
@ParameterizedTest
@CsvFileSource(files = "src/test/resources/csv-file-source_attributes.csv",delimiterString = "|",lineSeparator = "||",numLinesToSkip = 1)
void checkCsvFileSourceAttributes(int number, String expected) {assertEquals(StringUtils.equals(expected, "even")
? 0 : 1, number % 2);
}
src/test/resources/csv-file-source_attributes.csv
|| NUMBER | ODD_EVEN ||
|| 2 | even ||
|| 3 | odd ||
@EnumSource
該注解提供了一種方便的方法來使用枚舉常量作為測試用例參數。支持的屬性包括:
- value - 枚舉類類型,例如 ChronoUnit.class
package java.time.temporal;public enum ChronoUnit implements TemporalUnit {SECONDS("Seconds", Duration.ofSeconds(1)),MINUTES("Minutes", Duration.ofSeconds(60)),
HOURS("Hours", Duration.ofSeconds(3600)),DAYS("Days", Duration.ofSeconds(86400)),//12 other units
}
ChronoUnit 是一個包含標準日期周期單位的枚舉類型。
@ParameterizedTest
@EnumSource(ChronoUnit.class)
void checkEnumSourceValue(ChronoUnit unit) {
assertNotNull(unit);
}
在此示例中,@EnumSource 將傳遞所有16個 ChronoUnit 枚舉值作為參數。
- names - 枚舉常量的名稱或選擇名稱的正則表達式,例如 DAYS 或 ^.*DAYS$
@ParameterizedTest
@EnumSource(names = { "DAYS", "HOURS" })
void checkEnumSourceNames(ChronoUnit unit) {assertNotNull(unit);
}
@ArgumentsSource
該注解提供了一個自定義的可重用ArgumentsProvider。ArgumentsProvider的實現必須是外部類或靜態(tài)嵌套類。
- 外部參數提供程序
public class ArgumentsSourceTest {@ParameterizedTest@ArgumentsSource(ExternalArgumentsProvider.class)void checkExternalArgumentsSource(int number, String expected) {assertEquals(StringUtils.equals(expected, "even")? 0 : 1, number % 2,"Supplied number " + number +" is not an " + expected + " number");}
}public class ExternalArgumentsProvider implements ArgumentsProvider {@Overridepublic Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception {return Stream.of(Arguments.of(2, "even"),Arguments.of(3, "odd"));}
}
- 靜態(tài)嵌套參數提供程序
public class ArgumentsSourceTest {@ParameterizedTest@ArgumentsSource(NestedArgumentsProvider.class)void checkNestedArgumentsSource(int number, String expected) {assertEquals(StringUtils.equals(expected, "even")
? 0 : 1, number % 2,"Supplied number " + number +" is not an " + expected + " number");}static class NestedArgumentsProvider implements ArgumentsProvider {@Overridepublic Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception {return Stream.of(Arguments.of(2, "even"),Arguments.of(3, "odd"));}}
}
參數轉換
首先,想象一下如果沒有參數轉換,我們將不得不自己處理參數數據類型的問題。
源方法: Calculator 類
public int sum(int a, int b) {return a + b;
}
測試用例:
@ParameterizedTest
@CsvSource({ "10, 5, 15" })
void calculateSum(String num1, String num2, String expected) {int actual = calculator.sum(Integer.parseInt(num1),Integer.parseInt(num2));assertEquals(Integer.parseInt(expected), actual);
}
如果我們有String參數,而我們正在測試的源方法接受Integers,則在調用源方法之前,我們需要負責進行此轉換。
JUnit5 提供了不同的參數轉換方式
- 擴展原始類型轉換
@ParameterizedTest
@ValueSource(ints = { 2, 4 })
void checkWideningArgumentConversion(long number) {assertEquals(0, number % 2);
}
使用 @ValueSource(ints = { 1, 2, 3 }) 進行參數化測試時,可以聲明接受 int、long、float 或 double 類型的參數。
- 隱式轉換
@ParameterizedTest
@ValueSource(strings = "DAYS")
void checkImplicitArgumentConversion(ChronoUnit argument) {assertNotNull(argument.name());
}
JUnit5提供了幾個內置的隱式類型轉換器。轉換取決于聲明的方法參數類型。例如,用@ValueSource(strings = "DAYS")注釋的參數化測試會隱式轉換為類型ChronoUnit的參數。
- 回退字符串到對象的轉換
@ParameterizedTest
@ValueSource(strings = { "Name1", "Name2" })
void checkImplicitFallbackArgumentConversion(Person person) {assertNotNull(person.getName());
}public class Person {private String name;public Person(String name) {this.name = name;}//Getters & Setters
}
JUnit5提供了一個回退機制,用于自動將字符串轉換為給定目標類型,如果目標類型聲明了一個適用的工廠方法或工廠構造函數。例如,用@ValueSource(strings = { "Name1", "Name2" })注釋的參數化測試可以聲明接受一個類型為Person的參數,其中包含一個名為name且類型為string的單個字段。
- 顯式轉換
@ParameterizedTest
@ValueSource(ints = { 100 })
void checkExplicitArgumentConversion(@ConvertWith(StringSimpleArgumentConverter.class) String argument) {assertEquals("100", argument);
}public class StringSimpleArgumentConverter extends SimpleArgumentConverter {@Overrideprotected Object convert(Object source, Class<?> targetType)throws ArgumentConversionException {return String.valueOf(source);}
}
如果由于某種原因,您不想使用隱式參數轉換,則可以使用@ConvertWith注釋來定義自己的參數轉換器。例如,用@ValueSource(ints = { 100 })注釋的參數化測試可以聲明接受一個類型為String的參數,使用
StringSimpleArgumentConverter.class將整數轉換為字符串類型。
參數聚合
@ArgumentsAccessor
默認情況下,提供給@ParameterizedTest方法的每個參數對應于一個方法參數。因此,當提供大量參數的參數源可以導致大型方法簽名時,我們可以使用ArgumentsAccessor而不是聲明多個參數。類型轉換支持如上面的隱式轉換所述。
@ParameterizedTest
@CsvSource({ "John, 20","Harry, 30" })
void checkArgumentsAccessor(ArgumentsAccessor arguments) {Person person = new Person(arguments.getString(0),arguments.getInteger(1));assertTrue(person.getAge() > 19, person.getName() + " is a teenager");
}
自定義聚合器
我們看到ArgumentsAccessor可以直接訪問@ParameterizedTest方法的參數。如果我們想在多個測試中聲明相同的ArgumentsAccessor怎么辦?JUnit5通過提供自定義可重用的聚合器來解決此問題。
- @AggregateWith
@ParameterizedTest
@CsvSource({ "John, 20","Harry, 30" })
void checkArgumentsAggregator(@AggregateWith(PersonArgumentsAggregator.class) Person person) {assertTrue(person.getAge() > 19, person.getName() + " is a teenager");
}public class PersonArgumentsAggregator implements ArgumentsAggregator {@Overridepublic Object aggregateArguments(ArgumentsAccessor arguments,ParameterContext context) throws ArgumentsAggregationException {return new Person(arguments.getString(0),
arguments.getInteger(1));}
}
實現ArgumentsAggregator接口并通過@AggregateWith注釋在@ParameterizedTest方法中注冊它。當我們執(zhí)行測試時,它會將聚合結果作為對應測試的參數提供。ArgumentsAggregator的實現可以是外部類或靜態(tài)嵌套類。
額外福利
由于您已經閱讀完文章,我想給您一個額外的福利 - 如果您正在使用像Fluent assertions for java(AssertJ - fluent assertions java library)這樣的斷言框架,則可以將
java.util.function.Consumer作為參數傳遞,其中包含斷言本身。
@ParameterizedTest
@MethodSource("checkNumberArgs")
void checkNumber(int number, Consumer<Integer> consumer) {consumer.accept(number);
}static Stream<Arguments> checkNumberArgs() { Consumer<Integer> evenConsumer =i -> Assertions.assertThat(i % 2).isZero();Consumer<Integer> oddConsumer =i -> Assertions.assertThat(i % 2).isEqualTo(1);return Stream.of(Arguments.of(2, evenConsumer),Arguments.of(3, oddConsumer));
}
總結
JUnit5的參數化測試功能通過消除重復測試用例的需要,提供多次使用不同輸入運行相同測試的能力,實現了高效的測試。這不僅為開發(fā)團隊節(jié)省了時間和精力,而且還增加了測試過程的覆蓋范圍和有效性。此外,該功能允許對源代碼進行更全面的測試,因為可以使用更廣泛的輸入進行測試,從而增加了識別任何潛在的錯誤或問題的機會。總體而言,JUnit5的參數化測試是提高代碼質量和可靠性的有價值的工具。
最后感謝每一個認真閱讀我文章的人,看著粉絲一路的上漲和關注,禮尚往來總是要有的,雖然不是什么很值錢的東西,如果你用得到的話可以直接拿走!
軟件測試面試文檔
我們學習必然是為了找到高薪的工作,下面這些面試題是來自阿里、騰訊、字節(jié)等一線互聯(lián)網大廠最新的面試資料,并且有字節(jié)大佬給出了權威的解答,刷完這一套面試資料相信大家都能找到滿意的工作。
?