Writing a single unit test for multiple implementations of an interface
I have an interface List
whose implementations include Singly Linked List, Doubly, Circular etc. The unit tests I wrote for Singly should do good for most of Doubly as well as Circular and any other new implementation of the interface. So instead of repeating the unit tests for every implementation, does JUnit offer something inbuilt which would let me have one JUnit test and run it against different implementations?
Using JUnit parameterized tests I can supply different implementations like Singly, doubly, circular etc but for each implementation the same object is used to execute all the tests in the class.
I'd probably avoid JUnit's parameterized tests (which IMHO are pretty clumsily implemented), and just make an abstract List
test class which could be inherited by tests implementations:
public abstract class ListTestBase<T extends List> {
private T instance;
protected abstract T createInstance();
@Before
public void setUp() {
instance = createInstance();
}
@Test
public void testOneThing(){ /* ... */ }
@Test
public void testAnotherThing(){ /* ... */ }
}
The different implementations then get their own concrete classes:
class SinglyLinkedListTest extends ListTestBase<SinglyLinkedList> {
@Override
protected SinglyLinkedList createInstance(){
return new SinglyLinkedList();
}
}
class DoublyLinkedListTest extends ListTestBase<DoublyLinkedList> {
@Override
protected DoublyLinkedList createInstance(){
return new DoublyLinkedList();
}
}
The nice thing about doing it this way (instead of making one test class which tests all implementations) is that if there are some specific corner cases you'd like to test with one implementation, you can just add more tests to the specific test subclass.
With JUnit 4.0+ you can use parameterized tests:
- Add
@RunWith(value = Parameterized.class)
annotation to your test fixture - Create a
public static
method returningCollection
, annotate it with@Parameters
, and putSinglyLinkedList.class
,DoublyLinkedList.class
,CircularList.class
, etc. into that collection - Add a constructor to your test fixture that takes
Class
:public MyListTest(Class cl)
, and store theClass
in an instance variablelistClass
- In the
setUp
method or@Before
, useList testList = (List)listClass.newInstance();
With the above setup in place, the parameterized runner will make a new instance of your test fixture MyListTest
for each subclass that you provide in the @Parameters
method, letting you exercise the same test logic for every subclass that you need to test.