Unit: Impl or interface?

Suppose I have an interface and an implementation class that implements it, and I want to write unit-test for this. What should I check the interface or Impl?

Here is an example:

public interface HelloInterface { public void sayHello(); } public class HelloInterfaceImpl implements HelloInterface { private PrintStream target = System.out; @Override public void sayHello() { target.print("Hello World"); } public void setTarget(PrintStream target){ this.target = target; } } 

So, I have HelloInterface and HelloInterfaceImpl that implements it. What is an incomplete testing interface or Impl?

I think it should be HelloInterface. Consider the following JUnit test sketch:

 public class HelloInterfaceTest { private HelloInterface hi; @Before public void setUp() { hi = new HelloInterfaceImpl(); } @Test public void testDefaultBehaviourEndsNormally() { hi.sayHello(); // no NullPointerException here } @Test public void testCheckHelloWorld() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintStream target = new PrintStream(out); PrivilegedAccessor.setValue(hi, "target", target); //You can use ReflectionTestUtils in place of PrivilegedAccessor //really it is DI //((HelloInterfaceImpl)hi).setTarget(target); hi.sayHello(); String result = out.toString(); assertEquals("Hello World", result); } } 

The main line is the one I commented on.

((HelloInterfaceImpl)hi).setTarget(target);

The setTarget() method is not part of my public interface, so I donโ€™t want to accidentally call it. If I really want to call it, I should think about it. This helps me, for example, to discover that I'm really trying to inject an addiction. This opens up for me the whole world of new opportunities. I can use some existing dependency injection mechanism (for example, Spring), I can imitate it myself, as it actually was in my code, or use a completely different approach. Take a closer look, preparing PrintSream is not so easy, maybe I should use a mock object instead?

EDIT : I think I should always focus on the interface. From my point of view, setTarget() not part of the โ€œcontractโ€ of the impl class, and it serves to inject dependencies. I think that any public method of the Impl class should be considered private in terms of testing. This does not mean that I am ignoring implementation details.

See also. Should private / protected methods be under unit test?

EDIT-2 In the case of multiple implementations / multiple interfaces, I would test all implementations, but when I declare a variable in my setUp() method, I would definitely use the interface.

+21
java dependency-injection unit-testing mockito
Jun 07 '12 at 18:33
source share
4 answers

An implementation is a unit that needs to be tested. This, of course, is what you create and what contains the software / business logic.

If you had a critical interface, and you wanted to make sure that each implementation corresponds to it properly, you can write a test suite that focuses on the interface and requires that the instance be transferred (agnostic of any type of implementation).

Yes, it probably would have been easier to use Mockito for PrintStream, it is not always possible to avoid using a mock object, as in this particular example.

+13
Jun 07 2018-12-18T00:
source share

I would test the interface.

I think the error was that this entry was written in such a way that it was tightly associated with System.out; You could not override another PrintStream. I would use a constructor instead of a setter. There is no need for layout or casting in this way.

This is a simple case. I would suggest that a factory would be more complex to create different, more complex implementations of the interface. I hope you would not have designed it so that you were in the box.

Interface binding in your tests makes mocking a lot easier.

 public class HelloInterfaceImpl implements HelloInterface { private PrintStream target; public HelloInterfaceImpl() { this(System.out); } public HelloInterfaceImpl(PrintStream ps) { this.target = ps; } @Override public void sayHello() { target.print("Hello World"); } } 

Here's the test:

 public class HelloInterfaceTest { @Test public void testDefaultBehaviourEndsNormally() { HelloInterface hi = new HelloInterfaceImpl(); hi.sayHello(); } @Test public void testCheckHelloWorld() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintStream target = new PrintStream(out); HelloInterface hi = new HelloInterfaceImpl(target); hi.sayHello(); String result = out.toString(); assertEquals("Hello World", result); } } 
+6
Jun 07 '12 at 18:41
source share

I always test implementations - one class can implement several interfaces, and also one interface can be implemented by several classes - each of them should be covered by tests.

The requirement to call setter in unit test (casting an interface to an implementation):

 ((HelloInterfaceImpl)hi).setTarget(target); 

means you are really testing the implementation. This is not part of the contract, but it is an important part that makes the implementation work and should be checked properly.

Take an example from the JDK. You have a List interface and two implementations: ArrayList and LinkedList . Additionally, LinkedList implements the Deque interface. If you write a test for the List interface, what would you mention? Array or linked list? What else is the case of LinkedList , which interface would you choose for testing? Deque or List ? As you can see, when testing implementations you have no such problems.

For me personally, listing the interface for implementation in unit test is an obvious sign that something is going wrong;)

+5
Jun 07 2018-12-12T00:
source share

I would say that it depends on the implementation and what it does outside of the interface contract. Many implementations only implement the functionality provided in the interface; in others, the interface is only a small part of the functionality of the classes. It can implement multiple interfaces.

Ultimately, you are testing the implementation.

In the simple case, as you have determined, I say six out of a half dozen others. Write your test cases for an interface or implementation, if it tests the implementation enough, the results are the same.

Take another example, where I will have a class that collects statistics on the communication channel, decorating the reader and writer of the real one . My class can now implement these interfaces, but it also collects statistics that have nothing to do with the contract. I could, of course, write tests based on these interfaces, but he would not fully test this class.

+1
Jun 07 2018-12-12T00:
source share



All Articles