4

I am trying to pass in an array for testing a certain algorithm, but the arrays seem to not be passed correctly or at all. I manually tested the algorithm so I know it works as it's supposed to. How can I pass arrays in for testing in JUnit 5?

@ParameterizedTest
@CsvSource(value = {"[13,14,65,456,31,83],[1331,65456]"})
public void palindromeCombos(int[] input, int[] expected){
    Palindrome pal = new Palindrome();
    List<Integer> actual = pal.allPalindromes(input);
    int[] result = new int[actual.size()];
    for(int i = 0; i < actual.size(); i++){
         result[i] = actual.get(i);
    }
    Assertions.assertArrayEquals(expected, result);    
}
Mureinik
  • 297,002
  • 52
  • 306
  • 350
Noah Iarrobino
  • 1,435
  • 1
  • 10
  • 31
  • 1
    What are you expecting - one test execution that gets two arrays `[13,14,65,456,31,83]` and `[1331,65456]`? Something else? – Mureinik Dec 22 '20 at 21:40
  • the input is ```[13,14,65,456,31,83]``` and the expected output is ```[1331,65456]``` – Noah Iarrobino Dec 22 '20 at 21:42
  • So why don't you pass the input argument only and then assert if the output of the method is equal to the expected output (that you define within the test instead of passing it as an argument)? Or just define the input and expected output in the test, and pass the input to the method you want to test and then verify that the output equals the expected. This does not need to be a parameterized test at all, I think. – JustAnotherDeveloper Dec 22 '20 at 21:45
  • well because ideally I would pass many inputs and expected outputs, but I need to get 1 to work before I can move to 2 – Noah Iarrobino Dec 22 '20 at 21:46
  • You can do more than one test. Each test should test one case of the method. The normal case, and then the special cases: special case 1 (for example, passing negative values), special case 2 (passing in one or more null values in the array), etc. Because in each of those cases your method will probably do something different. With a parameterized test, the assertions should be the same for all the inputs. – JustAnotherDeveloper Dec 22 '20 at 21:50
  • I'm gonna post the rest of the test, I think it'll make more sense – Noah Iarrobino Dec 22 '20 at 21:51

2 Answers2

10

Pablo's Answer is correct, of course, but personally I'm not a fan of parsing strings if I don't absolutely have to. Another approach could be to use a MethodSource instead, and explicitly provide the arguments you need:

public static Stream<Arguments> palindromeCombos() {
    return Stream.of(
        Arguments.of(new int[]{13, 14, 65, 456, 31, 83}, new int[]{1331, 65456}));
}

@ParameterizedTest
@MethodSource
public void palindromeCombos(int[] input, int[] expected) {
    // Test logic...
}
Mureinik
  • 297,002
  • 52
  • 306
  • 350
3

Since there is not implicit conversion for arrays, you can use explicit conversion, first you need to declare you converter class:

    class IntArrayConverter implements ArgumentConverter {

        @Override
        public Object convert(Object source, ParameterContext context)
                throws ArgumentConversionException {
            if (!(source instanceof String)) {
                throw new IllegalArgumentException(
                        "The argument should be a string: " + source);
            }
            try {
                return Arrays.stream(((String) source).split(",")).mapToInt(Integer::parseInt).toArray();
            } catch (Exception e) {
                e.printStackTrace();
                throw new IllegalArgumentException("Failed to convert", e);
            }
        }
    }

Then you can use it in your test:

    @ParameterizedTest
    @CsvSource(value = {
            "13,14,65,456,31,83;1331,65456",
            "1,2,3,4,5,6;10,20"}, delimiterString = ";")
    public void palindromeCombos(@ConvertWith(IntArrayConverter.class) int[] input,
                                 @ConvertWith(IntArrayConverter.class) int[] expected) {
        System.out.println(Arrays.toString(input));
        System.out.println(Arrays.toString(expected));
    }

Notice that I removed the [] from the CsvSource and changed the delimiter to ;, so the arrays are expressed by a list of integers separated by comma. If you want you can keep the format you had and handle it in the converter class. For those two examples the output is:

[13, 14, 65, 456, 31, 83]
[1331, 65456]

[1, 2, 3, 4, 5, 6]
[10, 20]

If you need further information you can check this post: https://www.baeldung.com/parameterized-tests-junit-5