Unidad a prueba: ¿Impl o Interface?

Supongamos que tengo una interfaz y una clase de implementación que lo implementa y quiero escribir una prueba de unidad para esto. ¿Qué debo probar interfaz o Impl?

Aquí hay un ejemplo:

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;
    }
}

Por lo tanto, tengo HelloInterface y HelloInterfaceImpl que lo implementa. ¿Qué es la interfaz de unidad bajo prueba o Impl?

Creo que debería ser HelloInterface. Considere el siguiente boceto de la prueba JUnit:

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);

    }
 }

La línea principal es en realidad una que comenté.

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

MétodosetTarget() no es parte de mi interfaz pública, así que no quieroaccidentalmente llámalo. Si realmente quiero llamarlo, debería tomarme un momento y pensarlo. Me ayuda, por ejemplo, descubrir que lo que realmente estoy tratando de hacer es la inyección de dependencia. Me abre todo el mundo de nuevas oportunidades. Puedo usar algún mecanismo de inyección de dependencia existente (Spring's, por ejemplo), puedo simularlo como lo hice en mi código o para adoptar un enfoque totalmente diferente. Mirar más de cerca, la preparación de PrintSream no fue tan fácil, ¿tal vez debería usar un objeto simulado en su lugar?

EDITAR: Creo que deberíasiempre centrarse en la interfaz. Desde mi punto de vistasetTarget() no es parte del "contrato" de la clase impl, tampoco sirve para la inyección de dependencia. Creo que cualquier método público de clase Impl debería considerarse privado desde la perspectiva de la prueba. Sin embargo, no significa que ignore los detalles de la implementación.

Ver también¿Deben los métodos privados / protegidos estar bajo prueba de unidad?

EDIT-2 En el caso de implementaciones múltiples / interfaces múltiples, probaría todas las implementaciones, pero cuando declaro una variable en misetUp() Método que definitivamente usaría interfaz.

Respuestas a la pregunta(4)

Su respuesta a la pregunta