Более разумное параметрирование с помощью массива в Junit 5 (или другой тестовой библиотеке Java)

Я пытаюсь параметризовать этот тест:

@Test
public void reverseQuote(double[] qsp) throws Exception {
...}

Мне кажется абсурдным, что не существует какого-то быстрого метода для инициализации массива qsp как, например, ValueSource:

@ParameterizedTest
@ValueSource(ints = { 1, 2, 3 })
void testWithValueSource(int argument) {
    assertNotNull(argument);
}

моя цель - сделать что-то вроде @ValueSource(doublesArray = {new double[]{1.0, 2.0, 3.0}}) (теперь это возвращает ошибку). Не существует ничего, что позволяет что-то подобное?? Другие ответы, кажется, предлагают только сложные способы, например используя @MethodSource или @ConvertWith.

Я также принимаю ответы, реализующие другие библиотеки тестирования.


person Lore    schedule 14.02.2018    source источник
comment
не уверен, в чем проблема с использованием MethodSource.   -  person Vinay Prajapati    schedule 14.02.2018
comment
Из одного из ответов на ссылку, о которой я сообщил: используйте ArgumentSource (ваше решение), когда одни и те же сгенерированные тестовые примеры могут использоваться более чем одним тестовым классом. Используйте MethodSource (решение Sormuras), когда одни и те же сгенерированные тестовые примеры могут использоваться более чем одним методом тестирования (в одном классе). В противном случае постарайтесь, чтобы источник тестовых случаев был как можно более локальным по отношению к методу, который их использует.   -  person Lore    schedule 15.02.2018


Ответы (6)


Хорошо, это будет странный ответ, но он работает, и это было довольно весело.

Первое: твой путь невозможен. Не из-за JUnit или любого связанного API, а из-за Java - действительные элементы типа аннотации (аргументы аннотации могут быть только примитивными, String, Class, Enum, другими аннотациями и массивом всего этого).

Второе: мы можем обойти первое. Проверь это:

@ArraySources(
  arrays = {
    @ArraySource(array = {1, 2, 3}),
    @ArraySource(array = {4, 5, 6}),
    @ArraySource(array = {7, 8, 9})
  }
)

Как говорится, аннотация может иметь другие аннотации в качестве аргументов и их массивы, поэтому мы используем здесь эти 2 правила.

Третье: как это поможет? Мы можем добавить нашего собственного поставщика аннотаций + аргументов, JUnit 5 расширяется таким образом.

Обе аннотации:

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ArgumentsSource(ArrayArgumentsProvider.class)
public @interface ArraySources {
    ArraySource[] arrays();
}

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ArraySource {
    int[] array() default {};
}

Поставщик аргументов на основе аннотаций:

public class ArrayArgumentsProvider implements ArgumentsProvider, AnnotationConsumer<ArraySources> {
    private List<int[]> arguments;

    public void accept(ArraySources source) {
        List<ArraySource> arrays = Arrays.asList(source.arrays());

        this.arguments = arrays.stream().map(ArraySource::array).collect(Collectors.toList());
    }

    public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
        return this.arguments.stream().map(Arguments::of);
    }
}

И последний тест с использованием тех:

public class ArraySourcesTest {
    @ParameterizedTest
    @ArraySources(
            arrays = {
                    @ArraySource(array = {1, 2, 3}),
                    @ArraySource(array = {4, 5, 6}),
                    @ArraySource(array = {7, 8, 9})
            }
    )
    void example(int[] array) {
        System.out.println(Arrays.toString(array));
        System.out.println("Test Over");
    }
}

/* Output
[1, 2, 3]
Test Over
[4, 5, 6]
Test Over
[7, 8, 9]
Test Over
*/

Вы упомянули @MethodSource как сложный, ну так я думаю я не справился в этом вопросе, но он работает. Очевидно, его можно было бы упростить и улучшить (например, назвать аргументы аннотации как значения по умолчанию — значение — и я сделал это только для int, чтобы показать идею). Не уверен, что вы могли бы добиться того же с существующими функциями (ArgumentsProvider и ArgumentSources), но это выглядит более конкретно (вы знаете, что работаете с массивами) и показывает возможности расширения JUnit5, может быть полезно в другом случае.

person Shadov    schedule 02.08.2018

Возможно, следует рассмотреть возможность использования комбинации параметризованных тестов Junit и синтаксического анализа YAML.

@RunWith(Parameterized.class)
public class AnotherParameterizedTest {

    private final HashMap row;

    @Parameterized.Parameters(name="Reverse Lists Tests # {index}:")
    public static List<Map<String, Object>> data() {
        final TestData testData = new TestData(""+
             "|   ID   |       List         |  Expected   |                \n"+
             "|   0    |    [1, 2, 3]       |  [3, 2, 1]  |                \n"+
             "|   1    |    [2, 3, 5]       |  [3, 2, 1]  |                \n"+
             "|   2    |    [5, 6, 7]       |  [ 7, 6, 5] |                \n"
        );
        // parsing each row using simple YAML parser and create map per row
        return testData.getDataTable();
    }

    // Each row from the stringified table above will be 
    // split into key=value pairs where the value are parsed using a 
    // yaml parser. this way, values can be pretty much any yaml type
    // like a list of integers in this case. 
    public AnotherParameterizedTest(HashMap obj) {
        this.row = obj;
    }

    @Test
    public void test() throws Exception {
        List orgListReversed = new ArrayList((List) row.get("List"));
        Collections.reverse(orgListReversed);
        assertEquals((List) row.get("Expected"), orgListReversed);
    }

}

Вместо использования строки я использую программу чтения Excel, чтобы сделать то же самое с простыми таблицами Excel. Разбор каждой строки в одну карту с использованием YAML для значений.

Результаты тестирования Junit IDE

То же самое, только что протестированное с использованием Junit Jupiter, дает более хорошие результаты в IDE Runner.

import static org.junit.jupiter.api.Assertions.assertEquals;

import de.deicon.yatf.runner.dsl.TestData;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.Collections;
import java.util.List;
import java.util.Map;

public class FirstTest {

    @ParameterizedTest
    @MethodSource("testTable")
    public void test(Map row){
        List reversedList = (List) row.get("List");
        Collections.reverse(reversedList);
        assertEquals((List)row.get("Expected"), reversedList);
    }

    static List<Map<String, Object>> testTable() {
        return new TestData(""+
                "|ID|   List                  |Expected               |         \n"+
                "|0 | [1,2,3]                 | [3,2,1]               |         \n"+
                "|1 | [hans, peter, klaus]    | [klaus, peter, hans]  |         \n"
        ).getDataTable();
    }

}

введите описание изображения здесь

person Dieter    schedule 10.07.2018

Мне нравится использовать Spock для тестирования кода Java. Это тестовая среда на основе groovy, расположенная поверх JUnit 4. Параметризованные тесты в Spock — это встроенная функция:

def "The reverseQuote method doesn't return null"(double[] qsp) {

    when: "reverseQuote is called"
    double[] rev = reverseQuote(qsp)

    then: "the result is not null"
    null != rev

    where: "there are various input values"
    qsp << [
        [0.1, 0.2, 0.3] as double[],
        [1.0, 2.0, 3.0] as double[]
    ]
}

... в качестве альтернативы вы можете выложить свои тестовые данные в виде таблицы:

def "The reverseQuote method reverses the input array"(List qsp, List expected) {

    when: "reverseQuote is called"
    double[] rev = reverseQuote(qsp as double[])

    then: "the result is the reverse of the input"
    expected as double[] == rev

    where: "there are various input values"
    qsp             | expected
    [0.1, 0.2, 0.3] | [0.3, 0.2, 0.1]
    [1.0, 2.0, 3.0] | [3.0, 2.0, 1.0]
}

Обратите внимание, что преобладание as double[] является неблагоприятным следствием автоматического преобразования Groovy массивов в списки, поэтому мы должны явно отбрасывать их обратно в конкретных случаях, когда мы взаимодействуем с кодом Java, которому действительно требуется массив.

person user31601    schedule 10.07.2018

Множество:

    static Stream<Arguments> yourTest () {
    return Stream.of(
            Arguments.of((new int[] { 2, 1, 2, 3, 4 }), 3),
            Arguments.of((new int[] { 2, 2, 0 }), 3),
            Arguments.of((new int[]{1, 3, 5} ) ,0 )     
            );
}
// if not Array : List : Arguments.of((Arrays.asList(0, 1) ), 0.5)...


@ParameterizedTest(name = "{index} => array   = {0} ),  expected = {1} ") 
@MethodSource("yourTest")
void shoultCountEvens(  int[] array, int expected) {
    assertEquals( CountEvens.countEvens(array),  expected ); 
}


public class CountEvens {
public static int countEvens(int[] nums) {
    long count = Arrays.stream(nums)
    .filter(a -> a %  2 == 0)
    .count();
return Math.toIntExact(count);
}}
person Fox user9219598    schedule 23.01.2020

Документация JUnit предлагает использовать @MethodSource

@ParameterizedTest
@MethodSource("range")
void testWithRangeMethodSource(double argument) {
    assertNotEquals(9.0, argument);
}

static DoubleStream range() {
   return DoubleStream.range(0.0, 20.0);
}

В противном случае вы можете использовать это: https://junit.org/junit5/docs/5.0.2/api/org/junit/jupiter/params/provider/ValueSource.html#doubles

@ParameterizedTest
@ValueSource(doubles = { 1, 2, 3 })
public void test(double numberToTest){
   //do whatever
}
person Ben Sch    schedule 04.07.2018
comment
Можете ли вы привести пример того, как использовать второе решение? - person Lore; 04.07.2018
comment
на самом деле это не отвечает на вопрос: нужно создавать много массивов двойников, а не много двойников. - person Victor Noël; 08.07.2018

Вы можете проверить TestNg (я использую его в своем проекте). Пример в моем проекте вы можете посмотреть здесь введите описание ссылки здесь или ниже:

  @DataProvider(name = "processText")
  public Object[][] dataProvider() {
    return new Object[][]{
      new Object[]{"ala\nma\nkota", "grep ma", "ma"},
      new Object[]{"ala\nma\nkota", "grep -v ma", "ala\nkota"},
      new Object[]{"ala\nma\nkota", "cut -c1-3", "ala\nma\nkot"},
      //...
      new Object[]{"ala ma kota", "sed s/ma/XX/g", "ala XX kota"},
      new Object[]{"ala\nma\nkota", "grep -v ma | sed s/a/G/g", "GlG\nkotG"},
    };
  }

  @Test(dataProvider = "processText")
  public void testProcessText(String text, String cli, String expected) {
    final String actual = new UnixProcessing().processText(text, cli);
    assertEquals(actual, expected);
  }

Официальная документация TestNg находится здесь

person KrzyH    schedule 04.07.2018
comment
Не похоже на то, что я задал в вопросе. - person Lore; 04.07.2018