在JUnit 4中,通过使用@Parameterize
注释很容易跨一堆类测试不变量。关键是一组测试正在针对单个参数列表运行。
如何在JUnit 5中复制这一点,而不使用JUnit-vintage?
@ParameterizedTest
不适用于测试类。@TestTemplate
听起来可能是合适的,但该注释的目标也是一个方法。
此类 JUnit 4 测试的一个示例是:
@RunWith( Parameterized.class )
public class FooInvariantsTest{
@Parameterized.Parameters
public static Collection<Object[]> data(){
return new Arrays.asList(
new Object[]{ new CsvFoo() ),
new Object[]{ new SqlFoo() ),
new Object[]{ new XmlFoo() ),
);
}
private Foo fooUnderTest;
public FooInvariantsTest( Foo fooToTest ){
fooUnderTest = fooToTest;
}
@Test
public void testInvariant1(){
...
}
@Test
public void testInvariant2(){
...
}
}
JUnit 5中的参数化测试特性并不提供与JUnit 4完全相同的特性。< br >引入了更加灵活的新功能...但是它也失去了JUnit4的特性,参数化的测试类在类级别使用参数化的fixture/assertion,用于类的所有测试方法。< br >通过指定“输入”为每个测试方法定义< code>@ParameterizedTest是非常必要的。< br >除此之外,我将介绍两个版本之间的主要区别,以及如何在JUnit 5中使用参数化测试。
断续器
要编写一个参数化的测试,通过case指定一个值来测试您的问题,< code > org . JUnit . Jupiter . params . provider . method source 应该可以完成这项工作。
@MethodSource
允许您引用测试类的一个或多个方法。每个方法都必须返回流
、可迭代
、迭代器
或参数数组。此外,每个方法不得接受任何参数。默认情况下,此类方法必须是静态的,除非测试类使用 @TestInstance(Lifecycle.PER_CLASS)
进行批注。
如果只需要一个参数,则可以直接返回参数类型的实例,如以下示例所示。
与 JUnit 4 一样,@MethodSource
依赖于工厂方法,也可用于指定多个参数的测试方法。
在JUnit 5中,它是最接近JUnit 4的参数化测试编写方式。
JUnit 4:
@Parameters
public static Collection<Object[]> data() {
JUnit 5:
private static Stream<Arguments> data() {
主要改进:
>
<代码>集合
将工厂方法绑定到测试方法的方式略有不同。< br >现在它更短,更不容易出错:不再需要创建构造函数和声明字段来设置每个参数的值。源代码的绑定直接在测试方法的参数上完成。
对于 JUnit 4,在同一类中,必须使用 @Parameters
声明一个且只有一个工厂方法。
使用JUnit 5,这个限制被解除了:多种方法确实可以用作工厂方法。
因此,在类中,我们可以声明一些用@MethodSource(“..”)
注释的测试方法,这些方法引用不同的工厂方法。
例如,下面是一个示例测试类,它断言一些加法计算:
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;
import org.junit.jupiter.api.Assertions;
public class ParameterizedMethodSourceWithArgumentsTest {
@ParameterizedTest
@MethodSource("addFixture")
void add(int a, int b, int result) {
Assertions.assertEquals(result, a + b);
}
private static Stream<Arguments> addFixture() {
return Stream.of(
Arguments.of(1, 2, 3),
Arguments.of(4, -4, 0),
Arguments.of(-3, -3, -6));
}
}
要将现有的参数化测试从JUnit 4升级到JUnit 5,@omeodSource
是一个需要考虑的候选者。
总结
@MethodSource
有一些优点,但也有一些缺点。
JUnit 5 中引入了指定参数化测试源的新方法。
这里有一些关于它们的其他信息(远远详尽无遗),我希望这些信息可以就如何以一般方式处理提供广泛的想法。
导言
JUnit 5在以下术语中引入了参数化测试功能:
参数化测试使得使用不同的参数多次运行测试成为可能。它们的声明方式与常规@Test
方法类似,但使用@ParameterizedTest
注释。此外,还必须声明至少一个源,该源将为每个调用提供参数。
依赖性要求
参数化测试功能不包含在junit-jupiter-Engine
核心依赖项中。
您应该添加特定的依赖项才能使用它:junit-jupiter-params
。
如果使用Maven,这是要声明的依赖项:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.0.0</version>
<scope>test</scope>
</dependency>
可用于创建数据的源
与JUnit 4相反,JUnit 5提供了多种风格和工件来编写参数化测试
支持的方式通常取决于您要使用的数据源。
以下是框架提出并在文档中描述的源类型:
@ValueSource
@E num Source
@方法源
@Csv源代码
@Csv文件源
@参数源
以下是我在JUnit 5中实际使用的三个主要源代码,我将介绍:
@MethodSource
@ValueSource
@CsvSource
我认为它们是我编写参数化测试的基础。他们应该允许在JUnit 5中编写,你描述的JUnit 4测试类型。< br> @EnumSource
、< code>@ArgumentsSource和< code>@CsvFileSource当然会有所帮助,但它们更专业。
< code>@MethodSource 、< code>@ValueSource和< code>@CsvSource的演示
1) @MethodSource
这种类型的源需要定义一个工厂方法。< br >但它也提供了很大的灵活性。< br >
在JUnit 5中,它是最接近JUnit 4的参数化测试编写方式。
如果您在测试方法中有一个方法参数,并且希望使用任何类型作为源,@MethodSource
是一个很好的候选
要实现这一点,请定义一个方法,该方法为每种情况返回一个值流,并使用@MethodSource(“methodName”)
对测试方法进行注释,其中methodName
是此数据源方法的名称。
例如,您可以编写:
import java.util.stream.Stream;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class ParameterizedMethodSourceTest {
@ParameterizedTest
@MethodSource("getValue_is_never_null_fixture")
void getValue_is_never_null(Foo foo) {
Assertions.assertNotNull(foo.getValue());
}
private static Stream<Foo> getValue_is_never_null_fixture() {
return Stream.of(new CsvFoo(), new SqlFoo(), new XmlFoo());
}
}
如果测试方法中有多个方法参数,并且希望使用任何类型作为源,@MethodSource
也是一个非常好的候选方法。
为了实现它,定义一个方法,为每个要测试的案例返回一个流。
例如,您可以编写:
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;
import org.junit.jupiter.api.Assertions;
public class ParameterizedMethodSourceWithArgumentsTest {
@ParameterizedTest
@MethodSource("getFormatFixture")
void getFormat(Foo foo, String extension) {
Assertions.assertEquals(extension, foo.getExtension());
}
private static Stream<Arguments> getFormatFixture() {
return Stream.of(
Arguments.of(new SqlFoo(), ".sql"),
Arguments.of(new CsvFoo(), ".csv"),
Arguments.of(new XmlFoo(), ".xml"));
}
}
2)@ValueSource
如果您在测试方法中有一个方法参数,并且您可以从这些内置类型(字符串、整数、长、双精度)之一表示参数的源,@ValueSource
适合。
@ValueSource
确实定义了这些属性:
String[] strings() default {};
int[] ints() default {};
long[] longs() default {};
double[] doubles() default {};
例如,您可以这样使用它:
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
public class ParameterizedValueSourceTest {
@ParameterizedTest
@ValueSource(ints = { 1, 2, 3 })
void sillyTestWithValueSource(int argument) {
Assertions.assertNotNull(argument);
}
}
注意1)您不能指定多个注释属性。
注意2)源和方法参数之间的映射可以在两种不同的类型之间完成。
用作数据源的类型String
由于其解析,特别允许转换为多种其他类型。
3) @CsvSource
如果测试方法中有多个方法参数,则@CsvSource
可能适合。
若要使用它,请使用 @CsvSource
对测试进行批注,并在 String
数组中指定每种情况。
每个事例的值都用逗号分隔。
像< code>@ValueSource一样,方法的源和参数之间的映射可以在两个不同的类型之间完成。< br >下面是一个说明这一点的示例:
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
public class ParameterizedCsvSourceTest {
@ParameterizedTest
@CsvSource({ "12,3,4", "12,2,6" })
public void divideTest(int n, int d, int q) {
Assertions.assertEquals(q, n / d);
}
}
@CsvSource
与@MethodSource
这些源类型满足一个非常经典的要求:从源映射到测试方法中的多个方法参数。
但他们的方法不同。
@CsvSource
有一些优点 :它更清晰,更短。
实际上,参数的定义刚好高于测试方法,不需要创建可能另外产生“未使用”警告的夹具方法。
但它也有一个关于映射类型的重要限制。
您必须提供字符串
数组。该框架提供转换功能,但功能有限。
总而言之,作为源提供的<code>字符串
由于情况并非如此,您必须做出选择,要么通过为框架未执行的转换创建自定义转换器(ArgumentConverter
子类)来保持@CsvSource
的灵活性,要么使用@MethodSource
返回 Stream 的工厂方法
论元转换
关于源代码(
@CsvSource
或@ValueSource
)与测试方法参数之间的映射,如图所示,如果类型不相同,框架允许进行一些转换。
下面是两种类型转换的演示:
3.13.3.变元转换
隐式转换
为了支持
@CsvSource
等用例,JUnit Jupiter提供了许多内置的隐式类型转换器。转换过程取决于每个方法参数的声明类型。
……
String
实例当前隐式转换为以下目标类型。
Target Type | Example
boolean/Boolean | "true" → true
byte/Byte | "1" → (byte) 1
char/Character | "o" → 'o'
short/Short | "1" → (short) 1
int/Integer | "1" → 1
.....
例如,在前面的示例中,在源代码中的
String
和定义为参数的int
之间完成了隐式转换:
@CsvSource({ "12,3,4", "12,2,6" })
public void divideTest(int n, int d, int q) {
Assertions.assertEquals(q, n / d);
}
在这里,从
字符串
源到本地日期
参数的隐式转换完成:
@ParameterizedTest
@ValueSource(strings = { "2018-01-01", "2018-02-01", "2018-03-01" })
void testWithValueSource(LocalDate date) {
Assertions.assertTrue(date.getYear() == 2018);
}
如果对于两种类型,框架没有提供转换,这是自定义类型的情况,则应使用
ArgumentConverter
。
显式转换
您可以不使用隐式参数转换,而是使用< code>@ConvertWith
批注显式指定用于某个参数的< code>ArgumentConverter,如下例所示。
JUnit为需要创建特定ArgumentConverter
的客户端提供了参考实现。
显式参数转换器旨在由测试作者实现。因此,junit-jupiter-params 仅提供一个显式参数转换器,该转换器也可以用作参考实现:JavaTime参数转换器
。它通过组合注释 Java 时间转换模式使用
。
使用该转换器的测试方法:
@ParameterizedTest
@ValueSource(strings = { "01.01.2017", "31.12.2017" })
void testWithExplicitJavaTimeConverter(@JavaTimeConversionPattern("dd.MM.yyyy") LocalDate argument) {
assertEquals(2017, argument.getYear());
}
JavaTimeArgumentConverter
转换器类:
package org.junit.jupiter.params.converter;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.Year;
import java.time.YearMonth;
import java.time.ZonedDateTime;
import java.time.chrono.ChronoLocalDate;
import java.time.chrono.ChronoLocalDateTime;
import java.time.chrono.ChronoZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalQuery;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.junit.jupiter.params.support.AnnotationConsumer;
/**
* @since 5.0
*/
class JavaTimeArgumentConverter extends SimpleArgumentConverter
implements AnnotationConsumer<JavaTimeConversionPattern> {
private static final Map<Class<?>, TemporalQuery<?>> TEMPORAL_QUERIES;
static {
Map<Class<?>, TemporalQuery<?>> queries = new LinkedHashMap<>();
queries.put(ChronoLocalDate.class, ChronoLocalDate::from);
queries.put(ChronoLocalDateTime.class, ChronoLocalDateTime::from);
queries.put(ChronoZonedDateTime.class, ChronoZonedDateTime::from);
queries.put(LocalDate.class, LocalDate::from);
queries.put(LocalDateTime.class, LocalDateTime::from);
queries.put(LocalTime.class, LocalTime::from);
queries.put(OffsetDateTime.class, OffsetDateTime::from);
queries.put(OffsetTime.class, OffsetTime::from);
queries.put(Year.class, Year::from);
queries.put(YearMonth.class, YearMonth::from);
queries.put(ZonedDateTime.class, ZonedDateTime::from);
TEMPORAL_QUERIES = Collections.unmodifiableMap(queries);
}
private String pattern;
@Override
public void accept(JavaTimeConversionPattern annotation) {
pattern = annotation.value();
}
@Override
public Object convert(Object input, Class<?> targetClass) throws ArgumentConversionException {
if (!TEMPORAL_QUERIES.containsKey(targetClass)) {
throw new ArgumentConversionException("Cannot convert to " + targetClass.getName() + ": " + input);
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
TemporalQuery<?> temporalQuery = TEMPORAL_QUERIES.get(targetClass);
return formatter.parse(input.toString(), temporalQuery);
}
}
主要内容:1 参数化测试的介绍,2 使用@Parameter进行字段注入而不是构造函数,3 使用单个参数进行测试,4 识别单个测试用例1 参数化测试的介绍 自定义流道参数化实现参数化测试。运行参数化测试类时,将为测试方法和测试数据元素的叉积创建实例。 例如,要测试斐波那契函数,请编写: FibonacciTest的每个实例都将使用二元参数构造函数和方法中的数据值构造 @Parameters 。 2 使用@Parameter进行字段注入而不是构造函数 也可以将数据值直接注入字段中,而无需使用@Pa
问题内容: 在JUnit 4中,使用批注很容易在多个类中测试不变式。关键是要针对单个参数列表运行一组测试。 如何在不使用JUnit-vintage的情况下在JUnit 5中复制它? 不适用于测试课程。听起来似乎很合适,但是注释的目标也是一种方法。 此类JUnit 4测试的示例是: 问题答案: JUnit 5中的参数化测试功能所提供的 功能与JUnit 4所提供的功能完全不同。引入了具有更大灵活性的
我有一个接口,例如: 以及我试图测试的该接口的几个实现(例如、、)。 我的许多测试方法实际上是为了确保接口被正确实现,因此在每个实现中都是重复的。在JUnit3中,一个常见的解决方案是创建一个基类(扩展),然后由每个实现类对其进行子类化。但是对于JUnit4来说,这是正确的方法吗? 在(我相信)优先顺序的升序中可能的选择: > 剪切'n'粘贴重复的测试方法。一点也不枯燥,但我想测试中的担忧比生产代
我有一个工作环境,包括我不管理的bom和JUnit5测试,除非我从如果我从它们不会被maven surefire插件拾取。我阅读了许多部分,并在一个较小的项目中进行了测试,它的工作和拾取,我不知道该在这篇文章中包含什么,因此您有足够的信息来查看问题所在。此外,如果有人能解释我阅读的导入的幕后内容,JUnit 4和5都需要使用surefire即 与版本 我很感激。 注意到 < li >我的测试都在s
我正在尝试从CSV文件运行参数化测试。 如果我只使用这样的CSVSource,它就会起作用: 但如果我从一个文件中尝试同样的方法,它将不起作用: 我也尝试过使用硬路径访问我的文件,但对于Eclipse中的文件测试,我总是能得到消息 找不到使用测试runnter“JUnit 5”的测试。 JUnit期望文件在哪里? 以下是我的依赖关系: 有人知道我可能遗漏了什么或者错误在哪里吗? 提前谢谢你 保罗
我试图定义一个,如示例MockitoExtension所提供的,但无法成功地使用参数化的类实例。 期待着关于如何用在中实现接口的类的参数化实例测试接口的建议。