7

I am attempting to write a parameterized test for an interface Foo, which declares a method getFooEventInt(int, int). I have written a paramterized test that works for a single instance of Foo (a FooImpl object).

public class FooTest {

    @ParameterizedTest
    @MethodSource("getFooEvenIntProvider")
    public void getFooEvenIntTest(int seed, int expectedResult) {
        Foo foo = new FooImpl();

        Assertions.assertEquals(expectedResult, foo.getFooEvenInt(seed));
    }

    private static Stream getFooEvenIntProvider() {
        return Stream.of(
            Arguments.of(-2,  0),
            Arguments.of(-1,  0),
            Arguments.of( 0,  2),
            Arguments.of( 1,  2),
        );
    }
}

However, I'd like to be able to have getFooEvenIntTest(int, int) be invoked against a provided list of Foo implementation instances, with each iteration then using the provide list of seed/expectedResult values.

I realize I could do this as...

public class FooTest {

    @ParameterizedTest
    @MethodSource("getFooProvider")
    public void getFooImplEvenIntTest(Foo foo) {
        int[] expectedResult = {  0,  0, 2, 2 };
        int[] seed           = { -2, -1, 0, 1 };

        for(int i=0; i<seed.length; i++) {
            Assertions.assertEquals(expectedResult[i],
                                    foo.getFooEvenInt(seed[i]));
        }
     }

    private static Stream getFooProvider() {
        return Stream.of(
                Arguments.of(new FooImpl()),
                Arguments.of(new FooImpl2())
        );
    }
}

Any ideas? I'll post if I figure it out, but I thought I'd check to see if this is even doable, or if there's a different approach.

1

2 Answers 2

4

I guess you think of combining two arguments streams. You could achieve this by creating the cartesian product of two arguments lists.

I have implemented that on https://github.com/joerg-pfruender/junit-goodies/blob/master/src/main/java/com/github/joergpfruender/junitgoodies/ParameterizedTestHelper.java

public static Stream<Arguments> cartesian(Stream a, Stream b) {
  List argumentsA = (List) a.collect(Collectors.toList());
  List argumentsB = (List) b.collect(Collectors.toList());

  List<Arguments> result = new ArrayList();
  for (Object o : argumentsA) {
    Object[] objects = asArray(o);
    for (Object o1 : argumentsB) {
      Object[] objects1 = asArray(o1);
      Object[] arguments = ArrayUtils.addAll(objects, objects1);
      result.add(Arguments.of(arguments));
    }
  }
  return result.stream();
}

private static Object[] asArray(Object o) {
  Object[] objects;
  if (o instanceof Arguments) {
    objects = ((Arguments) o).get();
  } else {
    objects = new Object[]{o};
  }
  return objects;
}

Then your test code will be:

  public static Stream<Arguments> fooIntsAndFooProvider() {
    return ParameterizedTestHelper.cartesian(getFooEvenIntProvider(), getFooProvider());
  }

  @ParameterizedTest
  @MethodSource("fooIntsAndFooProvider")
  public void getFooImplEvenIntTest(Integer seed, Integer expectedResult, Foo foo) {
        Assertions.assertEquals(expectedResult,
                                foo.getFooEvenInt(seed));
  }
Sign up to request clarification or add additional context in comments.

Comments

2

BLUF: I will interpret the crickets to mean "even if you could, you shouldn't be nesting parameterized tests", in which case I run with the approach outlined below.

For an interface Foo...

public interface Foo {
    public char getFirstChar(String strValue);
    public int  getNextEvenInt(int seed);
}

The "best" use of parameterized tests for implementations of Foo would be...

public class FooTest {

    @ParameterizedTest
    @MethodSource("getFooProvider")
    public void getFirstCharTest(Foo foo) {
        char[]   expectedResult = { 'a', 'b', 'c', 'd' };
        String[] seed           = { "alpha", "bravo", "charlie", "delta" };

        for(int i=0; i<seed.length; i++) {
            Assertions.assertEquals(expectedResult[i],
                                    foo.getFirstChar(seed[i]));
        }
    }

    @ParameterizedTest
    @MethodSource("getFooProvider")
    public void getNextEvenIntTest(Foo foo) {
        int[] expectedResult = {  0,  0, 2, 2 };
        int[] seed           = { -2, -1, 0, 1 };

        for(int i=0; i<seed.length; i++) {
            Assertions.assertEquals(expectedResult[i],
                                    foo.getFooEvenInt(seed[i]));
        }
    }

    private static Stream getFooProvider() {
        return Stream.of(
                Arguments.of(new FooImplOne()),
                Arguments.of(new FooImplTwo())
                // extend as need for implementations of Foo
        );
    }
}

While I won't get the "warm fuzzies" of seeing the passing results for each value-pair in the various tests, it will fulfill my goal of having a test at the interface level that I can easily extend to validate/verify the interface's implementations.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.