Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/352.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何在JUnit5中实现JUnit4参数化测试?_Java_Junit5 - Fatal编程技术网

Java 如何在JUnit5中实现JUnit4参数化测试?

Java 如何在JUnit5中实现JUnit4参数化测试?,java,junit5,Java,Junit5,在JUnit4中,通过使用注释可以很容易地跨一组类测试不变量。关键的一点是,测试集合是针对单个参数列表运行的 如何在JUnit5中复制这一点,而不使用JUnit vintage 不适用于测试类。听起来可能很合适,但该注释的目标也是一种方法 JUnit 4测试的一个例子是: @RunWith( Parameterized.class ) public class FooInvariantsTest{ @Parameterized.Parameters public static C

在JUnit4中,通过使用注释可以很容易地跨一组类测试不变量。关键的一点是,测试集合是针对单个参数列表运行的

如何在JUnit5中复制这一点,而不使用JUnit vintage

不适用于测试类。听起来可能很合适,但该注释的目标也是一种方法


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(){
       ...
   } 
}
@RunWith(Parameterized.class)
公共类FooInvariantsTest{
@参数化。参数化
公共静态收集数据(){
返回新数组。asList(
新对象[]{new CsvFoo()),
新对象[]{new SqlFoo()),
新对象[]{new XmlFoo()),
);
}
私家富富测试不足;
公共Foo不变量测试(Foo fooToTest){
FoooUnderTest=FootTest;
}
@试验
公共无效测试变量1(){
...
}
@试验
公共无效测试变量2(){
...
} 
}

JUnit 5中的参数化测试功能与JUnit 4提供的功能并不完全相同。
引入了更具灵活性的新特性……但它也失去了JUnit4特性,在JUnit4特性中,参数化测试类在类级别使用参数化的fixture/assertions,该类用于类的所有测试方法。
需要通过指定“输入”为每个测试方法定义
@ParameterizedTest

除此之外,我将介绍这两个版本之间的主要区别,以及如何在JUnit5中使用参数化测试

TL;DR

要编写一个参数化测试,按案例指定要测试的值作为问题的答案, 我应该做这项工作

@MethodSource
允许您引用一种或多种测试方法 类。每个方法都必须返回
Iterable
迭代器
或数组 另外,每个方法都不能接受任何参数。 默认情况下,除非测试类是静态的,否则这些方法必须是静态的 用
@TestInstance(Lifecycle.PER_CLASS)
注释

如果只需要一个参数,则可以返回 参数类型,如下例所示

与JUnit4一样,
@MethodSource
依赖于工厂方法,也可用于指定多个参数的测试方法。

在JUnit5中,它是编写最接近JUnit4的参数化测试的方法。

JUnit4:

@Parameters
public static Collection<Object[]> data() {
< >将现有的参数化测试从JUnit 4升级到JUnit 5,<代码> @方法源是一个要考虑的候选对象。
总结

@MethodSource
有一些优点,但也有一些缺点。
JUnit5中引入了指定参数化测试源的新方法。
这里有一些关于这些问题的补充信息(非常详尽),我希望这些信息能让我大致了解如何处理这些问题。

导言

JUnit 5用以下术语介绍:

参数化测试可以使用多个参数多次运行测试 不同的参数。它们的声明方式与常规的
@Test
方法相同 但是请改用
@parameteriedtest
注释 必须声明至少一个将为提供参数的源 每次调用

依赖性要求

参数化测试功能不包括在junit jupiter引擎的核心依赖项中。

您应该添加一个特定的依赖项来使用它:
junitjupiter-params

如果使用Maven,这是要声明的依赖项:

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-params</artifactId>
    <version>5.0.0</version>
    <scope>test</scope>
</dependency>
如果测试方法中有多个方法参数,并且希望使用任何类型作为源,
@MethodSource
也是一个很好的候选方法。
为了实现它,定义一个方法,为每个要测试的案例返回一个
org.junit.jupiter.params.provider.Arguments

例如,您可以编写:

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());
    }

}
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"));
    }
}
例如,您可以这样使用它:

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)源和方法参数之间的映射可以在两种不同类型之间完成。
作为数据源使用的类型
字符串
,由于其解析,特别允许转换为多种其他类型。

3)
@CsvSource

如果测试方法中有多个方法参数,则可能适合使用
@CsvSource

要使用它,请使用
@CsvSource
注释测试,并在
字符串数组中指定每个大小写。
每个大小写的值用逗号分隔。

@ValueSource
类似,方法的源和参数之间的映射可以在两种不同类型之间完成。
以下示例说明:

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
VS
@MethodSource

这些源类型满足一个非常经典的要求:从源映射到测试方法中的多个方法参数。
但他们的方法不同。

@CsvSource
有一些优点:它更清晰、更短。
事实上,参数仅在测试方法上方定义,无需创建夹具方法,否则可能会生成“未使用”警告。
但它在映射类型方面也有一个重要的限制。
您必须提供一个字符串数组。框架提供了转换功能,但有限。

总之,作为源提供的
字符串
和测试方法的参数具有相同的类型(
字符串
->
字符串
)或
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);
    }

}
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);
    }

}
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
.....
@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);
}
@ParameterizedTest
@ValueSource(strings = { "01.01.2017", "31.12.2017" })
void testWithExplicitJavaTimeConverter(@JavaTimeConversionPattern("dd.MM.yyyy") LocalDate argument) {
    assertEquals(2017, argument.getYear());
}
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);
    }

}