junit-team / junit5

✅ The 5th major version of the programmer-friendly testing framework for Java and the JVM

Home Page:https://junit.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

@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 use MethodSource 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 a ParameterizedTest, 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?

https://junit.org/junit5/docs/snapshot/user-guide/#writing-tests-parameterized-tests-sources-FieldSource

@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"))
);