Search code examples
javajunit4junit5

How to test different implementations for an interface in Junit5 without duplicating the code


May I ask how to write a junit 5 test for an interface with different implementations?

For example, I have a interface Solution, with different implementations like SolutionI, SolutionII, can I write only one test class to test both?

There is a post shows an example, but if there are multiple test method that needs to be called, I have to pass the parameter for every test method.

May I ask if there is an elegant way like what is used in the Junit4

In Junit4, I have a very elegant code sample as follows

@RunWith(Parameterized.class)
public class SolutionTest {
  private Solution solution;

  public SolutionTest(Solution solution) {
    this.solution = solution;
  }

  @Parameterized.Parameters
  public static Collection<Object[]> getParameters() {
    return Arrays.asList(new Object[][]{
        {new SolutionI()},
        {new SolutionII()}
    });
  }
  // normal test methods
  @Test
  public void testMethod1() {

  }
}

Junit 5 claims ExtendWith() is similar, I tried the following code

@ExtendWith(SolutionTest.SolutionProvider.class)
public class SolutionTest {
  private Solution solution;

  public SolutionTest(Solution solution) {
    System.out.println("Call constructor");
    this.solution = solution;
  }

  @Test
  public void testOnlineCase1() {
    assertEquals(19, solution.testMethod(10));
  }

  @Test
  public void testOnlineCase2() {
    assertEquals(118, solution.testMethod(100));
  }

  static class SolutionProvider implements ParameterResolver {
    private final Solution[] solutions = {
        new SolutionI(),
        new SolutionII()
    };
    private static int i = 0;

    @Override
    public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
      return parameterContext.getParameter().getType() == Solution.class;
    }

    @Override
    public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
      System.out.println(i);
      return solutions[i++];
    }
  }
}

Unfortunately, testMethod1 is using SolutionI and testMethod2 is using SolutionII, which makes sense, I don't know if this helps to inspire a little bit.

Thanks for the help in advance


Solution

  • Sorry for not replying to this thread for a while. Comparing to the lotor's answer, I found some other ways I am currently adopting:

    
      @ParameterizedTest
      @MethodSource("solutionStream")
      void testCase(Solution solution) {
       // add your test
      }
    
      static Stream<Solution> solutionStream() {
        return Stream.of(
            new SolutionI(),
            new SolutionII()
        );
      }
    

    The constructor needs parameters (Not type-safe)

      @ParameterizedTest
      @MethodSource("solutionStream")
      void testOnlineCase(Class<Solution> solutionClass) throws NoSuchMethodException, IllegalAccessException,
          InvocationTargetException, InstantiationException {
        Solution solution = solutionClass.getConstructor(Integer.TYPE).newInstance(2);
      }
    
      static Stream<Class> solutionStream() {
        return Stream.of(
            SolutionI.class
        );
      }