What is the best way to write junit tests for interfaces so they can be used for the concrete implementing classes?
e.g. You have this interface and implementing classes:
public interface MyInterface { /** Return the given value. */ public boolean myMethod(boolean retVal); } public class MyClass1 implements MyInterface { public boolean myMethod(boolean retVal) { return retVal; } } public class MyClass2 implements MyInterface { public boolean myMethod(boolean retVal) { return retVal; } }
How would you write a test against the interface so you can use it for the class?
Possibility 1:
public abstract class MyInterfaceTest { public abstract MyInterface createInstance(); @Test public final void testMyMethod_True() { MyInterface instance = createInstance(); assertTrue(instance.myMethod(true)); } @Test public final void testMyMethod_False() { MyInterface instance = createInstance(); assertFalse(instance.myMethod(false)); } } public class MyClass1Test extends MyInterfaceTest { public MyInterface createInstance() { return new MyClass1(); } } public class MyClass2Test extends MyInterfaceTest { public MyInterface createInstance() { return new MyClass2(); } }
Pro:
Con:
Possibility 2:
public abstract class MyInterfaceTest public void testMyMethod_True(MyInterface instance) { assertTrue(instance.myMethod(true)); } public void testMyMethod_False(MyInterface instance) { assertFalse(instance.myMethod(false)); } } public class MyClass1Test extends MyInterfaceTest { @Test public void testMyMethod_True() { MyClass1 instance = new MyClass1(); super.testMyMethod_True(instance); } @Test public void testMyMethod_False() { MyClass1 instance = new MyClass1(); super.testMyMethod_False(instance); } } public class MyClass2Test extends MyInterfaceTest { @Test public void testMyMethod_True() { MyClass1 instance = new MyClass2(); super.testMyMethod_True(instance); } @Test public void testMyMethod_False() { MyClass1 instance = new MyClass2(); super.testMyMethod_False(instance); } }
Pro:
Con:
Which possibility would you prefer or what other way do you use?
You can generate a new JUnit test case for an interface that declares annotated methods, or you can update an existing JUnit test case for such an interface.
You can use an abstract test case to test an interface with common tests independent of implementation, and then generate concrete instances of the test case for each implementation of the interface.
To test an interface with common tests regardless of implementation, you can use an abstract test case, and then create concrete instances of the test case for each implementation of the interface.
Contrary to the much-voted-up answer that @dlev gave, it can sometimes be very useful/needful to write a test like you're suggesting. The public API of a class, as expressed through its interface, is the most important thing to test. That being said, I would use neither of the approaches you mentioned, but a Parameterized test instead, where the parameters are the implementations to be tested:
@RunWith(Parameterized.class) public class InterfaceTesting { public MyInterface myInterface; public InterfaceTesting(MyInterface myInterface) { this.myInterface = myInterface; } @Test public final void testMyMethod_True() { assertTrue(myInterface.myMethod(true)); } @Test public final void testMyMethod_False() { assertFalse(myInterface.myMethod(false)); } @Parameterized.Parameters public static Collection<Object[]> instancesToTest() { return Arrays.asList( new Object[]{new MyClass1()}, new Object[]{new MyClass2()} ); } }
I strongly disagree with @dlev. Very often it is a very good practice writing tests that use interfaces. Interface defines contract between client and the implementation. Very often all your implementations must pass exactly the same tests. Obviously each implementation can have its own tests.
So, I know 2 solutions.
Implement abstract test case with various tests that use interface. Declare abstract protected method that returns concrete instance. Now inherit this abstract class as many times as you need for each implementation of your interface and implement the mentioned factory method accordingly. You can add more specific tests here as well.
Use test suites.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With