Ошибка при тестировании

При выполнении этой части кода:

final String testCases = MethodSourceParametrizedTesting.testCases()
                .map(arg -> Arrays.stream(arg.get()).map(Object::toString).collect(joining(",")))
                .collect(joining(";"));

Возникает следующая ошибка:

java: cannot find symbol
  symbol:   method map((arg)->Arr[...]",")))
  location: interface java.lang.String[][]

Сам тест:

class MethodSourceParametrizedTesting {

    Factorial factorial = new Factorial();

    public static String[][] testCases() {
        return new String[][]{
                {"1","1"}, {"2", "2"}, {"5", "120"}};
    }

    @ParameterizedTest
    @MethodSource("testCases")
    void testFactorial(String in, String expected) {
        assertEquals(expected, factorial.factorial(in));
    }

}

Ответы (1 шт):

Автор решения: Михаил Ребров

Ошибка №1

java: cannot find symbol
symbol: method map((arg)->Arr[...]",")))
location: interface java.lang.String[][]

Он ругается на то, что вы пытаетесь вызвать метод map сразу на результате testCases()
Результатом вызова которого является двумерный массив и он такого метода не имеет
Его нужно обернуть Arrays.stream() и потом уже вызывать map()

Arrays
    .stream(MethodSourceParametrizedTesting.testCases())
    .map( /** ... */);

Ошибка №2

map(arg -> Arrays.stream(arg.get()) // ...

В данном случае лямбда получает на вход String[]
И опять же, массив не имеет метода get()
Тут мы просто убираем get() и передаем arg в stream() как есть

stream(arg)

Рабочий пример

final String testCases = Arrays.stream(MethodSourceParametrizedTesting.testCases())
                .map(arg -> Arrays.stream(arg).map(Object::toString).collect(joining(",")))
                .collect(joining(";"));
System.out.println(testCases);

out

1,1;2,2;5,120

UPDATE

возвращаем сразу Stream+Supplier

Так как мы не можем изменить

final String testCases = MethodSourceParametrizedTesting.testCases()
                .map(arg -> Arrays.stream(arg.get()).map(Object::toString).collect(joining(",")))
                .collect(joining(";"));

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

  1. для того чтобы при вызове testCases().map(/**...*/) не возникала ошибка нам следует не обернуть вызов testCases() в Arrays.stream(/**...*/), а сразу возвращать Stream из testCases()... тогда map будет работать нормально
  2. второй нашей проблемой был вызов arg.get()
    мы там имели String[] на входе, но как известно String[], не имеет метода get()...
    И подумав, что же они там могут ждать - я не придумал ничего лучше как функциональьный интерфейс Supplier, все что умеет который - это возвращать элементы с помощью метода get()
public static Stream<Supplier<String[]>> testCases() {
    return (Stream<Supplier<String[]>>)IntStream.range(1, 6).mapToObj((i)->{
        Integer current = i;
        Supplier<String[]> item  = () -> {
            Integer result = 1;
            for (int j = 1; j <= current ; j++) {
                result = result * j;
            }
            String[] pair = { Integer.toString(current), Integer.toString(result)};
            return pair;
        };
        return item;
        }
    );
}

Вывод:

1,1;2,2;3,6;4,24;5,120

Что соответствует факториалу

UPDATE 2

Замечание:
Если честно, то я старый дед и работал с Java задолго до выхода JDK 1.8 и появления Stream'ов
Знаю я их потольку-поскольку и не могу сказать что они прям очень плотно засели в моем арсенале, поэтому если что-то можно было сделать эффективнее и элегантнее - не серчайте

когда начал писать объяснения, понял, что лучше упростить все до нЕльзя - и Вам понятнее будет, и проще, и практичнее.

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

Собственно вся хитрость просто в том, что я

  • взял заранее определенный двумерный массив
  • сделал стрим на основе этого массива
  • и смапил Stream<String[]> который в качестве элементов имеет массив строк, в Stream<Supplier<String[]>> стрим каждый из элементов которого может получить массив строк с помощью функционального интерфейса Supplier<T>

Для последнего нужно просто вызвать метод map() и передать туда лямбду, которая будет приводить массив строк String[] в лямбду Supplier<String>

Делаем мы это так:

// получаем на вход String[] pair
pair -> { 
    // Объявляем лямбду, единственной задачей которой, является возвращение pair
    Supplier<String[]> item  = () -> pair;
    // возвращаем не pair, а лямбду, которая уже сможет вернуть pair
    // запутанно? - да!
    // а что вы хотели? такое условие задания
    return item;
}

И уже рабочий вариант метода:

public static Stream<Supplier<String[]>> testCases() {
    String[][] cases = new String[][]{
            {"1","1"}, {"2", "2"}, {"5", "120"}
        };
    return Arrays.stream(cases).map(pair -> {
        Supplier<String[]> item  = () -> pair;
        return item;
    });
}

Вывод:

1,1;2,2;5,120
→ Ссылка