从Junit5开始,对参数化测试支持进行了大幅度的改进和提升。下面我们就一起来详细看看Junit5参数化测试的方法。
部署和依赖
和Junit4相比,Junit5框架更多在向测试平台演进。其核心组成也从以前的一个Junit的jar包更换成由多个模块组成。本文所需要依赖模块如下:
- junit-jupiter-engine: Junit的核心测试引擎
- junit-jupiter-params: 编写参数化测试所需要的依赖包
- junit-platform-launcher: 从IDE(InteliJ/Eclipses)等运行时所需要的启动器
另外,为了从Maven命令行工具中运行Juint,还需要
junit-platform-surefire-provider包的依赖。
maven的pom.xml文件中添加如下来进行安装
<dependencies> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.2.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-params</artifactId> <version>5.2.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-launcher</artifactId> <version>1.2.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.22</version> </plugin> </plugins> </build>
Junit5 参数源详解
value source
value source是最简单的参数源,通过注解可以直接指定携带的运行参数。
- String values: @ValueSource(strings = {“foo”, “bar”, “baz”})
- Double values: @ValueSource(doubles = {1.5D, 2.2D, 3.0D})
- Long values: @ValueSource(longs = {2L, 4L, 8L})
- Integer values: @ValueSource(ints = {2, 4, 8})
示例代码如下:
import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; public class ValueSourcesExampleTest { @ParameterizedTest @ValueSource(ints = {2, 4, 8}) void testNumberShouldBeEven(int num) { assertEquals(0, num % 2); } @ParameterizedTest @ValueSource(strings = {"Radar", "Rotor", "Tenet", "Madam", "Racecar"}) void testStringShouldBePalindrome(String word) { assertEquals(isPalindrome(word), true); } @ParameterizedTest @ValueSource(doubles = {2.D, 4.D, 8.D}) void testDoubleNumberBeEven(double num) { assertEquals(0, num % 2); } boolean isPalindrome(String word) { return word.toLowerCase().equals(new StringBuffer(word.toLowerCase()).reverse().toString()); } }
输出
[INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running qiucao.learning.ParaTest [INFO] Tests run: 11, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.155 s - in qiucao.learning.ParaTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 11, Failures: 0, Errors: 0, Skipped: 0
Enum Source
枚举参数源,允许我们通过将参数值由给定Enum枚举类型传入。并可以通过制定约束条件或正则匹配来筛选传入参数
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.EnumSet; import java.util.concurrent.TimeUnit; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.EnumSource.Mode; public class EnumSourcesExampleTest { @ParameterizedTest(name = "[{index}] TimeUnit: {arguments}") @EnumSource(TimeUnit.class) void testTimeUnitMinimumNanos(TimeUnit unit) { assertTrue(unit.toMillis(L) > 1); } @ParameterizedTest @EnumSource(value = TimeUnit.class, names = {"SECONDS", "MINUTES"}) void testTimeUnitJustSecondsAndMinutes(TimeUnit unit) { assertTrue(EnumSet.of(TimeUnit.SECONDS, TimeUnit.MINUTES).contains(unit)); assertFalse(EnumSet .of(TimeUnit.DAYS, TimeUnit.HOURS, TimeUnit.MILLISECONDS, TimeUnit.NANOSECONDS, TimeUnit.MICROSECONDS).contains(unit)); } @ParameterizedTest @EnumSource(value = TimeUnit.class, mode = Mode.EXCLUDE, names = {"SECONDS", "MINUTES"}) void testTimeUnitExcludingSecondsAndMinutes(TimeUnit unit) { assertFalse(EnumSet.of(TimeUnit.SECONDS, TimeUnit.MINUTES).contains(unit)); assertTrue(EnumSet .of(TimeUnit.DAYS, TimeUnit.HOURS, TimeUnit.MILLISECONDS, TimeUnit.NANOSECONDS, TimeUnit.MICROSECONDS).contains(unit)); } @ParameterizedTest @EnumSource(value = TimeUnit.class, mode = Mode.MATCH_ALL, names = ".*SECONDS") void testTimeUnitIncludingAllTypesOfSecond(TimeUnit unit) { assertFalse(EnumSet.of(TimeUnit.DAYS, TimeUnit.HOURS, TimeUnit.MINUTES).contains(unit)); assertTrue(EnumSet .of(TimeUnit.SECONDS, TimeUnit.MILLISECONDS, TimeUnit.NANOSECONDS, TimeUnit.MICROSECONDS).contains(unit)); } }
输出:
[INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running qiucao.learning.ParaTest [INFO] Tests run: 18, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.206 s - in qiucao.learning.ParaTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 18, Failures: 0, Errors: 0, Skipped: 0
Method Source
通过其他的Java方法函数来作为参数源。引用的方法返回值必须是Stream, Iterator 或者Iterable.
import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.stream.IntStream; import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; public class MethodSourceExampleTest { @ParameterizedTest @MethodSource("stringGenerator") void shouldNotBeNullString(String arg){ assertNotNull(arg); } @ParameterizedTest @MethodSource("intGenerator") void shouldBeNumberWithinRange(int arg){ assertAll( () -> assertTrue(arg > 0), () -> assertTrue(arg <= 10) ); } @ParameterizedTest(name = "[{index}] user with id: {0} and name: {1}") @MethodSource("userGenerator") void shouldUserWithIdAndName(long id, String name){ assertNotNull(id); assertNotNull(name); } static Stream<String> stringGenerator(){ return Stream.of("hello", "world", "let's", "test"); } static IntStream intGenerator() { return IntStream.range(1,10); } static Stream<Arguments> userGenerator(){ return Stream.of(Arguments.of(1L, "Sally"), Arguments.of(2L, "Terry"), Arguments.of(3L, "Fred")); } }
输出:
[INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running qiucao.learning.ParaTest [INFO] Tests run: 16, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.191 s - in qiucao.learning.ParaTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 16, Failures: 0, Errors: 0, Skipped: 0
Argument Source
通过参数类来作为参数源。这里引用的类必须实现ArgumentsProvider接口。示例如下:
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.stream.Stream; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.ArgumentsProvider; import org.junit.jupiter.params.provider.ArgumentsSource; public class ArgumentsSourceExampleTest { @ParameterizedTest @ArgumentsSource(CustomArgumentsGenerator.class) void testGeneratedArguments(double number) throws Exception { assertFalse(number == 0.D); assertTrue(number > 0); assertTrue(number < 1); } static class CustomArgumentsGenerator implements ArgumentsProvider { @Override public Stream<? extends Arguments> provideArguments(ExtensionContext context) { return Stream.of(Math.random(), Math.random(), Math.random(), Math.random(), Math.random()) .map(Arguments::of); } } }
CSV Source
通过指定csv格式(comma-separated-values)的注解作为参数源
import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; public class CsvSourceExampleTest { Map<Long, String> idToUsername = new HashMap<>(); { idToUsername.put(1L, "Selma"); idToUsername.put(2L, "Lisa"); idToUsername.put(3L, "Tim"); } @ParameterizedTest @CsvSource({"1,Selma", "2,Lisa", "3,Tim"}) void testUsersFromCsv(long id, String name) { assertTrue(idToUsername.containsKey(id)); assertTrue(idToUsername.get(id).equals(name)); } }
输出:
[INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running qiucao.learning.ParaTest [INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.164 s - in qiucao.learning.ParaTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
CSV File Source
除了使用csv参数源,这里也支持使用csv文件作为参数源
假设users.csv 文件包含如下csv格式的数据
1,Selma
2,Lisa
3,Tim
代码示例如下
import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvFileSource; import org.junit.jupiter.params.provider.CsvSource; public class CsvFileSourceExampleTest { Map<Long, String> idToUsername = new HashMap<>(); { idToUsername.put(1L, "Selma"); idToUsername.put(2L, "Lisa"); idToUsername.put(3L, "Tim"); } @ParameterizedTest @CsvFileSource(resources = "/users.csv") void testUsersFromCsv(long id, String name) { assertTrue(idToUsername.containsKey(id)); assertTrue(idToUsername.get(id).equals(name)); } }
输出:
[INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running qiucao.learning.ParaTest [INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.199 s - in qiucao.learning.ParaTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
参数转换
JUnit allows us to convert arguments to the target format we need in our tests.
There are two possible conversion types:
隐式转换
JUnit提供了很对内建的格式转化支持,特别是string和常用的数据类型
以下是支持和string型进行转换的类型
Boolean
Byte
Character
Short
Integer
Long
Float
Double
Enum subclass
Instant
LocalDate
LocalDateTime
LocalTime
OffsetTime
OffsetDateTime
Year
YearMonth
ZonedDateTime
显式转换
Junit5中可以使用@ConvertWith(MyConverter.class) 注解来实现 SimpleArgumentConverter.
代码示例:
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.LocalDate; import java.time.Month; import java.util.UUID; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.converter.ConvertWith; import org.junit.jupiter.params.converter.SimpleArgumentConverter; import org.junit.jupiter.params.provider.ValueSource; public class ArgumentsConversionExampleTest { @ParameterizedTest @ValueSource(strings = "2017-07-11") void testImplicitArgumentConversion(LocalDate date) throws Exception { assertTrue(date.getYear() == 2017); assertTrue(date.getMonth().equals(Month.JULY)); assertTrue(date.getDayOfMonth() == 11); } @ParameterizedTest @ValueSource(strings = "B4627B3B-ACC4-44F6-A2EB-FCC94DAB79A5") void testImplicitArgumentConversion(@ConvertWith(ToUUIDArgumentConverter.class) UUID uuid) throws Exception { assertNotNull(uuid); assertTrue(uuid.getLeastSignificantBits() == -L); } static class ToUUIDArgumentConverter extends SimpleArgumentConverter { @Override protected Object convert(Object source, Class<?> targetType) { assertEquals(UUID.class, targetType, "may only convert to UUID"); return UUID.fromString(String.valueOf(source)); } } }
输出:
[INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running qiucao.learning.ParaTest [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.487 s - in qiucao.learning.ParaTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
补充对照: JUnit 4中参数化测试方法
代码如下:
@RunWith(Parameterized.class) public class ParameterizedTest { @Parameters(name = "Run #{index}: {0}^2={1}") public static Iterable<Object[]> data() { return Arrays.asList(new Object[][] { { 1, 1 }, { 2, 4 }, { 3, 9 }, { 4, 16 }, { 5, 25 } }); } private final int input; private final int resultExpected; public ParameterizedTest(final int input, final int result) { this.input = input; this.resultExpected = result; } @Test public void testUserMapping() { Calculator calc = new Calculator(); assertEquals(resultExpected, calc.square(input)); } }
2024最新激活全家桶教程,稳定运行到2099年,请移步至置顶文章:https://sigusoft.com/99576.html
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/17069.html