@ParameterizedTest @ArgumentSouce should accept Arguments[] and Stream<Arguments>
medwards opened this issue · comments
I'd like to use a list of Arguments
or a stream directly as a source for a ParameterizedTest
, without creating any additional methods or classes.
@ParameterizedTest
@ArgumentSource({
Arguments.of("foo", false, "A"),
Arguments.of("foo", true, "A"),
Arguments.of("bar", false, "B"),
Arguments.of("baz", true, "C")
})
void testABC(String firstInput, boolean secondInput, String expectedValue) {
assertEquals(f(firstInput, secondInput), expectedValue);
}
The current alternatives I can find from docs and experimentation are:
- Write an
ArgumentProvider
- Write a
MethodProvider
and useMethodSource
instead - Use
CsvSource
The trade-offs using these are:
- Additional class
- Additional method
- Have to deal with argument conversion (String to whatever the actual destination type is).
Arbitrary object instances and annotations?
https://docs.oracle.com/javase/specs/jls/se22/html/jls-9.html#jls-9.6.1
[...]
The return type of a method declared in the body of annotation interface must be one of the following, or a compile-time error occurs:A primitive type
String
Class or an invocation of Class (§4.5)
An enum class type
An annotation interface type
An array type whose component type is one of the preceding types (§10.1).
[...]
Or is this proposal about an @Arguments
annotation interface type?
Or is this proposal about an @arguments annotation interface type?
I believe this is what I'm shooting for - I'm not sure what about the best approach - but a new Source
annotation for this, or extending ValueSource
to accept arguments also seem workable to me.
So that would make it look like this, right?
@ParameterizedTest
@ArgumentSource({
@Arguments({"foo", false, "A"}),
@Arguments({"foo", true, "A"}),
@Arguments({"bar", false, "B"}),
@Arguments({"baz", true, "C"})
})
void testABC(String firstInput, boolean secondInput, String expectedValue) {
assertEquals(f(firstInput, secondInput), expectedValue);
}
Actually that doesn't work since Object[]
is not a valid type to use as an annotation attribute (see above JLS snippet), either.
I'd like to use a list of
Arguments
or a stream directly as a source for aParameterizedTest
, without creating any additional methods or classes.
That's simply not possible in Java: the Java language does not support arbitrary types as annotation attributes.
The current alternatives I can find from docs and experimentation are:
Have you seen the new @FieldSource
support coming in JUnit Jupiter 5.11?
@ParameterizedTest
@FieldSource("stringIntAndListArguments")
void testWithMultiArgFieldSource(String str, int num, List<String> list) {
assertEquals(5, str.length());
assertTrue(num >=1 && num <=2);
assertEquals(2, list.size());
}
static List<Arguments> stringIntAndListArguments = Arrays.asList(
arguments("apple", 1, Arrays.asList("a", "b")),
arguments("lemon", 2, Arrays.asList("x", "y"))
);
That's the least amount of boilerplate that we can support in an annotation in Java.
You still have to create a field, but that's less "boilerplate" than creating a method.
Thanks everyone for the feedback - I didn't follow the implication of the EBNF in the JLS so didn't realize my use-case was excluded.
FieldSource
looks like an improvement that I'll definitely use when it's available.
Thanks everyone for the feedback - I didn't follow the implication of the EBNF in the JLS so didn't realize my use-case was excluded.
No worries.
Probably all Java developers (including members of the JUnit team) wish that such things were possible with annotations.
FieldSource
looks like an improvement that I'll definitely use when it's available.
Glad to hear it, and for the record... I'm also looking forward to using it actively in my own tests.
The example in the User Guide is required to use Java 8 (Arrays.asList
), but on later versions of Java it becomes even more succinct (List.of
). 😎
static List<Arguments> stringIntAndListArguments = List.of(
arguments("apple", 1, List.of("a", "b")),
arguments("lemon", 2, List.of("x", "y"))
);