`ParameterResolutionException` for `Stream` of one-dimensional object array
augustindelecluse opened this issue · comments
Currently working with Junit 5, version 5.10.2. When working with parametrized tests and streams, the conversion between one object (say Integer
) into an array of objects (say Integer[]
) does not seem to work well. Instead of getting an array of objects, the value given as input is the first element of the array.
Steps to reproduce
public class ParameterIssueTests {
/**
* @return stream of array's containing 2 elements
*/
public static Stream<Integer[]> ArrayStreamKO() {
return IntStream.range(0, 2).mapToObj(i -> new Integer[] {i, i});
}
@ParameterizedTest
@MethodSource("ArrayStreamKO")
public void Bar(Integer[] array) {
// fails with the message
// org.junit.jupiter.api.extension.ParameterResolutionException: Error converting parameter at index 0:
// No built-in converter for source type java.lang.Integer and target type java.lang.Integer[]
}
@ParameterizedTest
@MethodSource("ArrayStreamKO")
public void Foo(Object o) {
// prints
// 0
// 1
// instead of [0, 0] and [1, 1]
System.out.println(o);
}
@Test
public void Baz() {
// prints
// [0, 0]
// [1, 1]
List<Integer[]> valueList = ArrayStreamKO().toList();
for (Integer[] array: valueList)
System.out.println(Arrays.toString(array));
}
}
Note that converting to Arguments
and a casting seems to fix the problem. Replacing the ArrayStreamKO
method by the following works, but requires the casting to Object
:
public static Stream<Arguments> ArrayStreamOK() {
return IntStream.range(0, 2).mapToObj(i -> Arguments.of((Object) new Integer[] {i, i}));
}
Without the casting to Object
, the same error as above arises.
Context
- Used versions (Jupiter/Vintage/Platform): Jupiter 5.10.2
- Build Tool/IDE: maven and IntelliJ
This is the expected behavior.
The Javadoc for @MethodSource
explicitly states:
Please note that a one-dimensional array of objects supplied as a set of "arguments" will be handled differently than other types of arguments. Specifically, all of the elements of a one-dimensional array of objects will be passed as individual physical arguments to the
@ParameterizedTest
method. This behavior can be seen in the table below for the staticStream<Object[]> factory()
method: the@ParameterizedTest
method accepts individualString
andint
arguments rather than a singleObject[]
array. In contrast, any multidimensional array supplied as a set of "arguments" will be passed as a single physical argument to the@ParameterizedTest
method without modification. This behavior can be seen in the table below for thestatic Stream<int[][]> factory()
andstatic Stream<Object[][]> factory()
methods: the@ParameterizedTest
methods for those factories accept individualint[][]
andObject[][]
arguments, respectively.
See also #1665.
You can use a custom ArgumentsAggregator
to achieve your goal as follows.
@ParameterizedTest
@MethodSource("arguments")
void test(@AggregateWith(IntegerArrayAggregator.class) Integer[] array) {
System.err.println(Arrays.toString(array));
}
static Stream<Integer[]> arguments() {
return IntStream.range(0, 2).mapToObj(i -> new Integer[] { i, i });
}
static class IntegerArrayAggregator implements ArgumentsAggregator {
@Override
public Integer[] aggregateArguments(ArgumentsAccessor arguments, ParameterContext context) {
return arguments.toList().stream().map(Integer.class::cast).toArray(Integer[]::new);
}
}
Running that prints:
[0, 0]
[1, 1]
The example I provided above allows you to keep the Stream<Integer[]>
return type for your factory method.
However, as you mentioned, the other option is to change the return type to Stream<Arguments>
and cast the array to an Object
and wrap that in an Arguments
instance, as you did in:
public static Stream<Arguments> ArrayStreamOK() {
return IntStream.range(0, 2).mapToObj(i -> Arguments.of((Object) new Integer[] {i, i}));
}
@augustindelecluse, do you have a specific requirement to use Integer[]
?
If not, switching to a primitive integer array (int[]
) is actually the easiest solution.
@ParameterizedTest
@MethodSource("ints")
void test(int[] array) {
System.out.println(Arrays.toString(array));
}
static Stream<int[]> ints() {
return IntStream.range(0, 2).mapToObj(i -> new int[] { i, i });
}
Running that also prints:
[0, 0]
[1, 1]