11

I want to run the same JUnit tests for different interface implementations. I found a nice solution with the @Parameter option:

public class InterfaceTest{

  MyInterface interface;

  public InterfaceTest(MyInterface interface) {
    this.interface = interface;
  }

  @Parameters
  public static Collection<Object[]> getParameters()
  {
    return Arrays.asList(new Object[][] {
      { new GoodInterfaceImpl() },
      { new AnotherInterfaceImpl() }
    });
  }
}

This test would be run twice, first with the GoodInterfaceImpl then with the AnotherInterfaceImpl class. But the problem is I need for most of the testcases a new object. A simplified example:

@Test
public void isEmptyTest(){
   assertTrue(interface.isEmpty());
}

@Test
public void insertTest(){
   interface.insert(new Object());
   assertFalse(interface.isEmpty());
}

If the isEmptyTest is run after the insertTest it fails.

Is there an option to run automatically each testcase with a new instance of an implementation?

BTW: Implementing a clear() or reset()-method for the interface is not really an options since I would not need it in productive code.

Maurice Müller
  • 1,312
  • 3
  • 16
  • 32
  • I suggest looking into dependency injection. Here is a small article on how do use it to accomplish exactly what you're looking for http://www.javaranch.com/journal/200709/dependency-injection-unit-testing.html – Benjamin Gruenbaum Jun 18 '12 at 09:37
  • Thanks for your quick answer! But this article describes how to break dependencies on an interface. I want to test the interface (not the class that uses the interface, or to speak in terms of the article: I want to test the remote farm [respectively the engines] not the farm servlet [respectively the cars]). – Maurice Müller Jun 18 '12 at 09:52

4 Answers4

8

Here is another approach with the Template Method pattern:

The interface-oriented tests go into the base class:

public abstract class MyInterfaceTest {

    private MyInterface myInterface;

    protected abstract MyInterface makeContractSubject();

    @Before
    public void setUp() {
        myInterface = makeContractSubject();
    }

    @Test
    public void isEmptyTest(){
        assertTrue(myInterface.isEmpty());
    }

    @Test
    public void insertTest(){
        myInterface.insert(new Object());
        assertFalse(myInterface.isEmpty());
    }
}

For each concrete class, define a concrete test class:

public class GoodInterfaceImplTest extends MyInterfaceTest {

    @Override
    protected MyInterface makeContractSubject() {
        // initialize new GoodInterfaceImpl
        // insert proper stubs
        return ...;
    }

    @Test
    public void additionalImplementationSpecificStuff() {
        ...
    }
}

A slight advantage over @Parameter is that you get the name of the concrete test class reported when a test fails, so you know right away which implementation failed.

Btw, in order for this approach to work at all, the interface must be designed in a way which allows testing by the interface methods only. This implies state-based testing -- you cannot verify mocks in the base test class. If you need to verify mocks in implementation-specific tests, these tests must go into the concrete test classes.

user1909402
  • 481
  • 5
  • 4
  • +1 for another good approach :) But regarding the class names, it is now possible (JUnit >= 4.11) to give different names to the tests, though it is not very handy to use -> http://stackoverflow.com/questions/650894/changing-names-of-parameterized-tests (s. the highest rated answer, not the accepted). Still, I like your solution, because you can add implementation-specific tests into derived test classes. Something I need an extra class otherwise. – Maurice Müller Mar 01 '13 at 08:18
6

Create a factory interface and implementations, possibly only in your test hierarchy if you don't need such a thing in production, and make getParameters() return a list of factories.

Then you can invoke the factory in a @Before annotated method to get a new instance of your actual class under test for each test method run.

Don Roby
  • 40,677
  • 6
  • 91
  • 113
1

Just in case somebody reaches here(like I did), looking for testing multiple implementations of the same interface in .net you could see one of the approaches that I was using in one of the projects here

Below is what we are following in short

The same test project dll is run twice using vstest.console, by setting an environment variable. Inside the test, (either in the assembly initialize or test initialize) register the appropriate implementations into a IoC container, based on the environment variable value.

Rahul P Nath
  • 324
  • 4
  • 9
1

In Junit 5 you could do:

@ParameterizedTest
@MethodSource("myInterfaceProvider")
void test(MyInterface myInterface) {}

static Stream<MyInterface> myInterfaceProvider() {
   return Stream.of(new ImplA(), new ImplB());
}

interface MyInterface {}

static class ImplA implements MyInterface {}

static class ImplB implements MyInterface {}
cnmuc
  • 6,025
  • 2
  • 24
  • 29
  • Thats nice! The only thing missing with this approach is if ImplA and ImplB needs different parameters for construction... haven't found a good solution for this... – oae Nov 28 '18 at 14:21