Alexander Asmer
Alexander Asmer

Reputation: 11

How to pass multiple arguments to a test method with a @MethodSource annotation and multiple methods that provide Stream<Arguments>?

I have a Junit5 parameterised test method and I need to use my custom DataProvider class with methods that return Stream<Arguments> and are supposed to pass all their arguments to this test method altogether. The test method itself has the following signature:

public void testAllSteps(String section, String subsection, String rangeFilterName, String min, String max, String ListFilterName, List<String> values, int number)

and DataProvider contains the following methods:

public static Stream<String> sectionProvider() {
    return Stream.of("Electronics", "Laptops");
}

public static Stream<String> rangeFilterProvider() { 
    return Stream.of("Price", "10000", "30000"); 
}

 
public static Stream<Arguments> listFilterProvider() {
    return Stream.of(
            Arguments.of("Manufacturer", List.of("HP, Lenovo")))
    );
}

public static Stream<Arguments> conditionNumberProvider() { 
    return Stream.of(Arguments.of(12)) 
}

Thus sectionProvider() provides values for section and subsection parameters, and the other ones provide values for the other respective parameters. Such segregation is due to grouping the arguments into logic groups.

I know that I can state multiple methods in the @MethodSource annotation for parameterising the method, which I tried to do, like this:

  @MethodSource({"utils.DataProvider#sectionProvider", "utils.DataProvider#rangeFilterProvider", "utils.DataProvider#listFilterProvider", "utils.DataProvider#conditionNumberProvider"})

but my program seems to fail to do that, saying the following:

org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [java.lang.String arg1] in method [public void market.MarketTest.testAllSteps(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.util.List<java.lang.String>,int)].

I'm still kinda new to Junit5 parameterised testing, and I don't how pass all this data to my test method altogether.

I suspect I'll have to split my test method implementation into multiple methods (since it's presented in Allure steps) so I can provide a separate portion of arguments to each of them, and it will work, but according to the whole task I was given, it's not the right thing to do.

Maybe you have some ideas to share?

I tried to use a method that combines all Streams of Arguments into one but that also resulted into an error.

Also I tried to reduce the number of arguments in my test by grouping them into collections so it would be equal to the number of provider methods in the @MethodSource statement, but it made some code in other classes really clumsy, and I wasn't sure it could help, so I refused that idea before completing.

Upvotes: 1

Views: 340

Answers (2)

talex
talex

Reputation: 20541

Here is a little snippet of test that combines 3 Arguments into one.

public class AppTest {

    @ParameterizedTest
    @MethodSource("all")
    public void test(String a, String b, String c) {
        System.out.printf("a = %s b = %s c = %s%n", a, b, c);
    }

    public static Stream<Arguments> first() {
        return Stream.of(
                Arguments.arguments("f1"),
                Arguments.arguments("f2"),
                Arguments.arguments("f3")
        );
    }

    public static Stream<Arguments> second() {
        return Stream.of(
                Arguments.arguments("s1"),
                Arguments.arguments("s2"),
                Arguments.arguments("s3")
        );
    }

    public static Stream<Arguments> third() {
        return Stream.of(
                Arguments.arguments("t1"),
                Arguments.arguments("t2"),
                Arguments.arguments("t3")
        );
    }

    public static Stream<Arguments> all() {
        return zipArguments(first(), second(), third());
    }

    private static Stream<Arguments> zipArguments(Stream<Arguments> first, Stream<Arguments>... other) {
        return Stream.of(other)
                .reduce(first, AppTest::zipArguments);
    }

    private static Stream<Arguments> zipArguments(Stream<Arguments> first, Stream<Arguments> second) {
        return zip(first, second, (f, s) ->
                Arguments.of(Stream.concat(
                                Arrays.stream(f.get()),
                                Arrays.stream(s.get()))
                        .toArray(Object[]::new)));
    }

    public static <L, R, T> Stream<T> zip(Stream<L> leftStream, Stream<R> rightStream, BiFunction<L, R, T> combiner) {
        Spliterator<L> lefts = leftStream.spliterator();
        Spliterator<R> rights = rightStream.spliterator();
        return StreamSupport.stream(new Spliterators.AbstractSpliterator<T>(Long.min(lefts.estimateSize(), rights.estimateSize()), lefts.characteristics() & rights.characteristics()) {
            @Override
            public boolean tryAdvance(Consumer<? super T> action) {
                return lefts.tryAdvance(left -> rights.tryAdvance(right -> action.accept(combiner.apply(left, right))));
            }
        }, false);
    }
}

Upvotes: 0

sanurah
sanurah

Reputation: 1132

May be reading this would help you: https://www.baeldung.com/parameterized-tests-junit-5

For example:

@MethodSource("utils.DataProvider#paramProvider")
public void testAllSteps(String section, String subsection, String rangeFilterName, String min, String max, String ListFilterName, List<String> values, int number)

then you can provide the params like this,

public static Stream<String> paramProvider() {
    return Stream.of(
            Arguments.of(
                    "Electronics",
                    "Laptops",
                    "Price",
                    "10000",
                    "30000",
                    "Manufacturer",
                    List.of("HP, Lenovo"),
                    12
            )
    );
}

Upvotes: -1

Related Questions