如何实现JUnit 4个中测试参数JUnit中5?

SLED:

在JUnit 4它是通过使用容易穿过一堆类测试不变量@Parameterized注释。关键的一点是,测试的集合被反对的论据单一列表运行。

如何复制这一JUnit中5,不使用JUnit复古?

@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(){
       ...
   } 
}
davidxxx:

JUnit中5的参数测试功能并没有提供比JUnit 4中提供的完全一样的功能,
新的更大的灵活性特点进行了介绍...但它同时也失去了JUnit4特征,其中参数化的测试类使用参数化夹具/断言在类级别是为类的所有测试方法。
限定@ParameterizedTest为通过指定“输入”每个测试方法是这样需要。
除此之外,缺少我会出现2个版本,以及如何使用参数化测试使用JUnit 5之间的主要差异。

TL; DR

要编写一个参数化测试通过的情况下指定的值来测试作为你在你的问题,org.junit.jupiter.params.provider.MethodSource应该做的工作。

@MethodSource让你指的是测试类的一个或多个方法。每个方法必须返回一个StreamIterableIterator,或的参数阵列。另外,每个方法必须不接受任何参数。默认情况下,这样的方法必须是静态的,除非测试类都被注解@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() {

主要改进:

  • Collection<Object[]>更是成为Stream<Arguments>提供更多的灵活性。

  • 的工厂方法结合到测试方法的方式不同的一点。
    现在是短且不易出错:没有更多的要求,以创建一个构造函数,并宣布现场设置每个参数的值。源的结合是直接对测试方法的参数来完成。

  • 使用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,@MethodSource是要考虑的候选人。


总结

@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
  • @EnumSource
  • @MethodSource
  • @CsvSource
  • @CsvFileSource
  • @ArgumentsSource

这里有3个主要来源其实我使用JUnit 5使用,我将介绍:

  • @MethodSource
  • @ValueSource
  • @CsvSource

我认为他们基本是我写的参数测试。他们应该允许的JUnit 5写的JUnit 4个测试类型,你描述。
@EnumSource@ArgumentsSource以及@CsvFileSource可能的当然是有用的,但他们更专业。

演示@MethodSource@ValueSource@CsvSource

1) @MethodSource

这种类型的源的需要,以限定一个工厂方法。
但它也提供了更大的灵活性。

在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也是一个非常不错的人选。
为了实现它,定义返回的物流的方法,org.junit.jupiter.params.provider.Arguments对于每种情况下测试。

例如,你可以写:

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每种情况下。
每种情况下的值则通过逗号隔开。

@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该框架提供转换功能,但它是有限的。

为了总结,而String设置作为源和测试方法的参数具有相同的类型(String- > String)或依赖于内置的转化率(String- > int例如),@CsvSource显示为方式使用。

因为它是不是这样的,你必须做出保持灵活性之间的选择@CsvSource通过创建一个自定义转换器(ArgumentConverter不通过框架进行转换的子类),或者使用@MethodSource一个工厂方法,返回Stream<Arguments>
其具有上述的缺点,但它也有很大的好处绘制出的最盒从源到的参数的任何类型。

参转换

关于源(之间的映射@CsvSource@ValueSource例如)和测试方法的参数,如看到的那样,框架允许做一些转换如果类型不一样的。

下面是两种类型转换的介绍:

3.13.3。参转换

隐式转换

为了支持使用情况一样@CsvSource,JUnit的木星提供了内置的隐式类型转换器为多。转换过程依赖于声明的类型的每个方法的参数。

.....

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

而在这里,隐式转换是从做StringLocalDate参数:

@ParameterizedTest
@ValueSource(strings = { "2018-01-01", "2018-02-01", "2018-03-01" })
void testWithValueSource(LocalDate date) {
    Assertions.assertTrue(date.getYear() == 2018);
}

如果两种类型,没有转换是由框架,这是自定义类型的情况下提供的,你应该使用的ArgumentConverter

显式转换

而不是使用隐含参数转换可能会明确指定ArgumentConverter使用使用某个参数@ConvertWith就像下面这个例子注解。

JUnit提供了谁需要创建一个特定的客户端的参考实现ArgumentConverter

明确说法转换器是为了通过测试的作者来实现。因此,仅junit的-木星-PARAMS提供单个显式的参数转换器,其也可以用作参考实现:JavaTimeArgumentConverter它通过由注解使用JavaTimeConversionPattern

使用该转换器测试方法:

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

}

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章