Unidade em teste: Impl ou Interface?

Suponha que eu tenha uma interface e uma classe de implementação que a implemente e eu queira escrever um teste de unidade para isso. O que devo testar interface ou Impl?

Aqui está um exemplo:

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

Então, eu tenho HelloInterface e HelloInterfaceImpl que o implementa. O que é interface unit-under-test ou Impl?

Eu acho que deveria ser HelloInterface. Considere o seguinte esboço do teste 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);

    }
 }

A linha principal é na verdade uma que eu comentei.

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

MétodosetTarget() não faz parte da minha interface pública, então eu não queroacidentalmente chame-o. Se eu realmente quiser chamá-lo, devo pensar e pensar um pouco. Ajuda-me, por exemplo, a descobrir que o que estou realmente tentando fazer é injeção de dependência. Abre-me todo o mundo de novas oportunidades. Eu posso usar algum mecanismo de injeção de dependência existente (o da Spring, por exemplo), eu mesmo posso simular como na verdade fiz no meu código ou ter uma abordagem totalmente diferente. Dê uma olhada mais de perto, a preparação do PrintSream não foi tão fácil, talvez eu deva usar mock object em vez disso?

EDITAR: Eu acho que eu deveriasempre concentre-se na interface. Do meu ponto de vistasetTarget() não faz parte do "contrato" da classe impl, nem serve para injeção de dependência. Eu acho que qualquer método público da classe Impl deve ser considerado como privado da perspectiva de teste. Isso não significa que eu ignore os detalhes da implementação, no entanto.

Veja tambémOs métodos privados / protegidos devem estar sob teste de unidade?

EDIT-2 No caso de múltiplas implementações \ interfaces múltiplas, gostaria de testar todas as implementações, mas quando declaro uma variável na minhasetUp() método eu definitivamente usaria interface.

questionAnswers(4)

yourAnswerToTheQuestion