Search code examples
javajunit

Function<T, R> as additional argument in Junit 5


I am trying to pass test arguments to JUnit 5 that include Java's Function<T, R> interface type as shown in the following code snippet. I am getting a "Target type of a lambda conversion must be an interface" error because JUnit 5's Arguments type uses an Object array to store the parameters.

  @ParameterizedTest(name = "Test description: {0}")
  @MethodSource("generateArguments")
  void passFunctionAsAnArgumentTest(final String description, final Function<String, String> testMethod) {

    // Fetch initial preactivation value.
    final String result = testMethod.apply("input");
    assertThat(result, is("expected"));
  }

  private static Stream<Arguments> generateArguments() {

    return Stream.of(
        // Error: "Target type of a lambda conversion must be an interface".
        Arguments.of("Return 'expected' if argument is 'input'", (String input) -> generateStringFromInput(input))
    );
  }

  private static String generateStringFromInput(final String input) {

    return input.equals("input") ? "expected" : "wrong";
  }

If I generate a stream that only uses the Function<T, R> interface type the test compiles and runs fine.

  @ParameterizedTest(name = "No test description :(")
  @MethodSource("generateStream")
  void passFunctionAsAnArgumentTest(final Function<String, String> testMethod) {

    // Fetch initial preactivation value.
    final String result = testMethod.apply("input");
    assertThat(result, is("expected"));
  }

  private static Stream<Function<String, String>> generateStream() {

    return Stream.of(
        JUnitFeatureTest::generateStringFromInput
    );
  }

  private static String generateStringFromInput(final String input) {

    return input.equals("input") ? "expected" : "wrong";
  }

Obviously the actual code is a bit more complex. The reason why I want to pass functions instead of test data objects is because we call different methods with a different set of parameters. It is just a lot tidier to encapsulate that in helper methods.


Solution

  • You need to tell the compiler which sort of lambda you intend to implent.
    So you need to cast your lambda to a Function<String, String> like this:

    private static Stream<Arguments> generateArguments() {
      return Stream.of(
          Arguments.of(
              "Return 'expected' if argument is 'input'",
              (Function<String, String>) JUnitFeatureTest::generateStringFromInput
          )
      );
    }
    

    or this

    private static Stream<Arguments> generateArguments() {
      return Stream.of(
          Arguments.of(
              "Return 'expected' if argument is 'input'", 
              (Function<String, String>) input -> generateStringFromInput(input)
          )
      );
    }