How to write junit tests for interfaces?

What is the best way to write junit tests for interfaces so that they can be used for specific implementation classes?

eg. You have this interface and the 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 could you write a test against an interface so you can use it for a class?

Option 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:

  • Only one method is needed to implement

Con:

  • Dependencies and mock objects of the tested class should be the same for all tests

Opportunity 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:

  • fine granulation for each test, including dependencies and layouts of objects

Con:

  • Each testing class needs to write additional testing methods.

What opportunity do you prefer or what other way do you use?

+69
java unit-testing junit interface testing
Jul 17 2018-11-17T00:
source share
6 answers

Contrary to the widely accepted answer given by @dlev, it is sometimes very useful / necessary to write a test as you suggest. The public class API, expressed through its interface, is the most important thing to test. In this case, I would not use any of the approaches you mentioned, but instead of Parameterized , where parameters are the tested implementations instead

 @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()} ); } } 
+73
Jul 17 2018-11-17T00:
source share

I strongly disagree with @dlev. Very often, it is very good practice to write tests that use interfaces. The interface defines the contract between the client and the implementation. Very often, all your implementations must pass the exact same tests. Obviously, each implementation can have its own tests.

So, I know 2 solutions.

  • Implement an abstract test case with various tests that use the interface. Declare an abstract protected method that returns a specific instance. Now inherit this abstract class as many times as needed for each implementation of your interface, and implement the factory method mentioned accordingly. Here you can add more specific tests.

  • Use test packages .

+18
Jul 17 2018-11-17T00:
source share

I also disagree with dlev, there is nothing wrong with writing tests on interfaces, and not on specific implementations.

You probably want to use parameterized tests. Here's what it would look like with TestNG , it is a little more thoughtful with JUnit (since you cannot pass parameters directly to test functions):

 @DataProvider public Object[][] dp() { return new Object[][] { new Object[] { new MyImpl1() }, new Object[] { new MyImpl2() }, } } @Test(dataProvider = "dp") public void f(MyInterface itf) { // will be called, with a different implementation each time } 
+14
Jul 17 2018-11-21T00:
source share

Late addition to the topic, sharing newer solutions

I am also looking for the right and efficient way to test (based on JUnit) the correctness of several implementations of some interfaces and abstract classes. Unfortunately, neither the JUnit @Parameterized tests, nor the equivalent TestNG concept meet my requirements, since I do not know a priori the list of implementations of these interface / abstract classes that may exist. That is, new implementations may be developed, and testers may not have access to all existing implementations; therefore, it is inefficient that test classes indicate a list of implementation classes.

At this point, I found the following project, which seems to offer a complete and effective solution to simplify this type of test: https://github.com/Claudenw/junit-contracts . Basically, this allows you to define "Contract Tests" through the @Contract(InterfaceClass.class) annotation @Contract(InterfaceClass.class) on test contract classes. Then the developer will create an implementation-specific test class with annotations @RunWith(ContractSuite.class) and @ContractImpl(value = ImplementationClass.class) ; the engine automatically applies any contract test that applies to the Class implementation by searching for all contract tests defined for any interface or abstract class from which the ClassClass implementation comes from. I have not tested this solution yet, but it sounds promising.

I also found the following library: http://www.jqno.nl/equalsverifier/ . This satisfies a similar, albeit much more specific, need, which asserts that the class conforms, in particular, with the contracts Object.equals and Object.hashcode.

Similarly, https://bitbucket.org/chas678/testhelpers/src demonstrates a strategy for validating some of the core Java contracts, including Object.equals, Object.hashcode, Comparable.compare, Serializable. This project uses simple test structures, which I believe can be easily reproduced to meet any specific needs.

Good thing for now; I will keep this post updated with other useful information I can find.

+10
Aug 6 '15 at 15:56
source share

I would generally avoid writing unit tests with an interface for the simple reason that the interface, no matter how you like it, does not define functionality. He circumvents his developers with syntactic requirements, but what is he.

Unit tests, by contrast, are designed to ensure that the functionality you expect is present in a given code path.

Speaking of this, there are situations when this type of test can make sense. Assuming you wanted these tests to ensure that the classes you wrote (that use this interface) actually have the same functionality, then I would prefer your first option. This facilitates the implementation of subclasses for implementation in the testing process. Also, I don't think your "con" is really right. There is no reason why you cannot have the classes that are actually tested, provide their own layouts (although I think that if you really need different layouts, this suggests that your interface tests are not uniform.)

+5
Jul 17 2018-11-17T00:
source share

with java 8 i do it

 public interface MyInterfaceTest { public MyInterface createInstance(); @Test default void testMyMethod_True() { MyInterface instance = createInstance(); assertTrue(instance.myMethod(true)); } @Test default void testMyMethod_False() { MyInterface instance = createInstance(); assertFalse(instance.myMethod(false)); } } public class MyClass1Test implements MyInterfaceTest { public MyInterface createInstance() { return new MyClass1(); } } public class MyClass2Test implements MyInterfaceTest { public MyInterface createInstance() { return new MyClass2(); } @Disabled @Override @Test public void testMyMethod_True() { MyInterfaceTest.super.testMyMethod_True(); }; } 
+1
Mar 06 '18 at 17:54
source share



All Articles