1

I have a problem with single/multiple arrays as varargs parameters used in JUnit5 MethodSource's Arguments.

If multiple arrays (empty or not) are passed, everything's fine, but if only a single array is passed, the Arguments.of() varargsunwraps the array and the originally intended as-array source is destroyed, which is not what I want. The problem relates to any varargs method parameters, not just to Arguments.

Method Arguments source:

static Object[] objectsWithNull = new Object[] { null }; // -> [null] intended array test argument

public static final List<Arguments> objSource() {
    return List.of(
            Arguments.of(objectsWithNull) // varargsArguments.of(Object...) unwraps array, not desired -> null
    );
}

public static final List<Arguments> objSources() {
    return List.of(
            Arguments.of(objectsWithNull, objectsWithNull) // -> [null], [null] OK
    );
}

Test method utilizing MethodSource arguments in 'TestClasses':

@ParameterizedTest
@MethodSource("objSource")
public <T> void myTest(final T t2) {
    assertDoesNotThrow(() -> { // test code
    });
}

@ParameterizedTest
@MethodSource("objSources")
public <T> void myTest(final T t1, final T t2) {
    assertDoesNotThrow(() -> { // test code
    });
}

Actually desired: -> [null] as in objSources

Arguments.of(objectsWithNull,objectsWithNull); // -> [null], [null] OK

Even worse, while for multiple empty arrays there's no problem:

Object[] objectsEmpty = new Object[0]; 
Arguments.of(objectsEmpty,objectsEmpty); // -> [], [] OK

A single empty array will throw org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [T arg0] in method ... [public void ... TestClasses.myTest(T,T)]:

Arguments.of(objectsEmpty); // -> throws 

Even more confusing, while using a typed null Object reference works, using a plain null value or a typed null array reference will throw an Exception:

static Object nullObjRef = (Object) null;
static Object[] nullArrayRef = (Object[]) null;

public static final List<Arguments> objSource() {
    return List.of(
            Arguments.of(nullObjRef), // #A -> OK
            Arguments.of(nullArrayRef), // #B -> throws org.junit.platform.commons.PreconditionViolationException: argument array must not be null
            Arguments.of(null) // #C -> throws org.junit.platform.commons.PreconditionViolationException: argument array must not be null
    );
}

While passing a null array reference causing an internal NPE in #B might be understandable, #A vs. #C (typed null reference vs. plain null) is not quite.

Also, a single empty array throwing a ParameterResolutionException seems rather random to me (while probably just a caught internal NPE or ArrayIndexOutOfBoundsException), instead of just passing through the empty array.

Finally, I'd like to know, if there's an easy, clean way of avoiding the unwrapping effect of single array arguments by varargs, as described in the first example, without the inconvenience of double-wrapping and unwrapping single or multiple arrays from inside a container object, which is passed to Arguments.of().

EDIT: When testing this, I've also found, that passing an array into a varargs parameter, the result is an array-wrapped array. Didn't cross such issues before trying to pass on arrays as test values per JUnit Arguments and MethodSource, though. Maybe reading this first would've helped: Passing arrays with varargs

Anyway, these effects make using Arguments in conjunction with arrays more difficult.

fozzybear
  • 91
  • 9

1 Answers1

0

To be clear, JUnit doesn't unwrap the array. JUnit uses reflection to call test cases with or without method parameters. This is the ambiguous part.

java.lang.reflect.Method.invoke signature is:

public Object invoke(Object obj, Object... args) {
// ...
}

When a method called via reflection the parameters passed as an Object array. JUnit supports multiple signature to provide parameters to test cases. Because of this, it is hard to decide what is the best. JUnit passes parameters as it got by the provider.

Workaround

Honestly I don't this this is the bulletproof solution, but it works. Sine 5.8 JUnit supports named payloads to provide arguments to test case. This factory method wraps the arguments. Named provider is designed for naming parameterized test executions. This is why I'm saying the following code is just a working workaround.

static List<Arguments> objSource() {
    return List.of(Arguments.arguments(
            Named.named("Null array", objectsWithNull))
    );
}
zforgo
  • 2,508
  • 2
  • 14
  • 22