JUnit5 multiple sources for different arguments (cartesian product)
Asked Answered
L

1

9

I am trying to write a test with JUnit 5 which should test multiple combinations of some parameters. Essentially I want to test some cartesian product of inputs from different sources. Consider the following test:

import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvFileSource;
class CartesianProductTest {

    @ParameterizedTest
    @CsvFileSource(resources = { "values.csv" })
    void testIt(int input, int expected, int otherParameter) {
        assertEquals(expected, methodUnderTest(input, otherParameter));
    }
}

The problem is now that I only have input and expected in the values.csv and the otherParameter should be tested for some fixed values that methodUnderTest() always returns the expected value for all these values. Somehow I have to provide a cartesian product of all the values in my CSV and all the values otherParameter can take. I looked at https://mcmap.net/q/947444/-parameterize-both-class-and-tests-in-junit-5 but this needs to either hardcode all my test cases or read the CSV manually to provide a Stream of the values. I thought more of something like

import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.CsvFileSource;
class CartesianProductTest {

    @ParameterizedTest
    @MethodSource
    void testIt(int input, int expected, int otherParameter) {
        assertEquals(expected, methodUnderTest(input, otherParameter));
    }

    static Stream<Arguments> testIt() {
         return csvValues().flatMap(csv ->
             otherParams().map(otherParam -> Arguments.of(csv, otherParam)));
    }

    @CsvFileSource(resources = { "values.csv" })
    static Stream<Arguments> csvValues() {/* get somehow from Annotation */}
    @CsvFileSource(resources = { "otherparam.csv" })
    static Stream<Arguments> otherParams() {/* get somehow from Annotation */}
}
Lamee answered 25/8, 2020 at 11:35 Comment(0)
O
1

For my use-case I was able to do something like this:

import com.google.common.collect.Lists;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class SampleTest {

    @ParameterizedTest
    @MethodSource
    void testSomething(List<String> list) {
        System.out.println(list);
        // do something
        // assert something
    }

    private static Stream<List<String>> testSomething() {
        List<String> one = Arrays.asList("waz", "yaz", "zaz");
        List<String> two = Arrays.asList("foo", "bar");
        List<List<String>> product = Lists.cartesianProduct(one, two);
        return product.stream();
    }
}

You would have to read your .csv into some List or something similar. Of course getting the name of the .csv from an annotation could be a challenge; maybe Custom Annotation could help, but I did not dig that far.

Possibly also worth looking at: Introduce ArgumentsProvider that computes the cartesian product of collections of datapoints, which will lead you to JUnit Pioneer.

Ouch answered 2/2, 2023 at 20:20 Comment(2)
JUnit Pioneer is what I always see when people start talking about caresian products. Worth expanding out into its own full, separate answerParable
@M.Justin Unfortunately JUnit Pioneer is not an option for me. Something about corporate security policy. :(Ouch

© 2022 - 2024 — McMap. All rights reserved.