Como lidar com código duplicado em Testes Automatizados?

Sou novo com testes automatizados.
Vou apresentar um caso de exemplo parecido com o que tenho aqui:

Preciso testar um ObjetoX que tem um Construtor que recebe um IComponenteX, assim:

public class ObjetoX {
    public ObjectoX(IComponenteX cx) {...}
    public int metodoA() {...}
    public void metodoB(int i) {...}
}

O IComponenteX é uma interface que até agora possui duas implementações, CompX1 e CompX2.

Quero escrever um Teste que irá testar os métodos de ObjetoX instanciando-o como new ObjetoX(new CompX1());, e depois quero testar os métodos do ObjetoX instanciando-o como new ObjetoX(new CompX2());.

Criei uma Classe de Teste assim:

public Teste_ObjetoX {
     private ObjetoX objX;
     @Before public void setUp() {objX = new ObjetoX(new CompX1());} //roda antes de cada Teste
     @Test public void testMetodoA() {...}
     @Test public void testMetodoB() {...}
}

Com a Classe de Teste acima consigo testar a implementação "new ObjetoX(new CompX1());", mas, ainda não consigo testar a implementação "new ObjetoX(new CompX2());".

Eu poderia fazer uma cópia da Classe de Teste e mudar apenas a implementação do método setUp(), mas isso criaria muito código repetido. Talvez eu devesse extender então essa classe de Teste e sobrescrever o método setUp(), mas será que isso é uma boa ideia?:

Por enquanto, os métodos testMetodoA() e testMetodoB() podem ser os mesmos para testar o ObjetoX construído com CompX1 ou com CompX2, porque os resultados esperados são os mesmos. Talvez isso mude no futuro.

Então, como faço? Como vocês fazem?

Se são implementações diferentes então não é repetido. Se acha que é repetido então por que tem duas implementações?

Concordo com o @javaflex e adiciono que, testes devem cobrir todos os possíveis cenários, inclusive, os unitários (no teu caso, já passa de unitário, afinal, você não está testando a menor unidade executável possível, está executando com dependências).
Basicamente, você precisa esquecer o que tem o @Before ou criar duas variáveis, uma para cada implementação.
Só lembrando que os testes devem cobrir todos os cenários, isso vai fazer com que você tenha, para cada método, ao menos 2 casos de testes: o sucesso e o erro.

No meu caso, CompX1 e CompX2 são implementações diferentes para fazer o mesmo trabalho, só muda o jeito de fazer por questões de performance. Então, se eu construir o ObjetoX com qualquer uma dessas dependências ele deve passar exatamente nos mesmos testes, pois são testes de caixa preta pra testar o Comportamento.

Como o @darlan_machado disse, já está além de teste unitário. E é aí que surge a dúvida:

Se eu posso construir o Objeto com várias dependências diferentes e quero testá-lo com várias combinações diferentes de dependências, como eu reaproveito os Testes de caixa-preta que testam esse Objeto?

No meu caso específico, até agora, posso aplicar exatamente os mesmos Testes sobre o ObjetoX independentemente da dependência (CompX1 ou CompX2) que eu escolher. O que eu fiz foi o seguinte:

Criei uma Classe de Teste que estende Teste_ObjetoX e sobrescreve o método setUp() para testar o ObjetoX com a dependência CompX2:

public Teste_ObjetoX_Com_CompX2 extends Teste_ObjetoX  {
     //Variável objX tornou-se protected na Superclasse para ser "setada" aqui
     @Before public void setUp() {objX = new ObjetoX(new CompX2());} //roda antes de cada Teste desta Subclasse
}

Algo legal é que - se no futuro eu precisar - eu poderei sobrescrever qualquer método de teste da Superclasse se quiser mudar a testagem que é feita sobre a configuração new ObjetoX(new CompX2());, sem afetar a testagem que já está sendo feita sobre a configuração da Superclasse que é new ObjetoX(new CompX1());.

Mas isso não me parece uma abordagem sustentável quando se pode ter muitas dependências diferentes e se quer testar o Objeto com várias combinações diferentes de dependências, pois para cada Combinação que se deseja testar seria necessário criar uma Subclasse da Classe de Teste, podendo levar a criação de um monte de Subclasses de Teste.

Talvez eu precisasse ter uma classe que constrói várias instâncias do Objeto, cada uma com uma combinação de dependências diferentes, e depois envia cada uma dessas instâncias para uma “bateria de Testes” (um conjunto de métodos de Teste) específica que irá testar a instância recebida por ela. Será que dá pra fazer isso? Será que iria ficar bom?

Como vocês fazem pra testar um Objeto com várias combinações diferentes de dependências, reaproveitando os métodos de Teste sempre que possível?

Está muito confuso e abstrato seu caso apresentado. Quando for assim apresente o código concreto.

Considerando do que já trabalhei com testes funcionais via Selenium, cada funcionalidade é testada separadamente e o reaproveitamento de código é natural, são simplesmente funções independentes que fazem algo de comum, como por exemplo UploadUtil.RealizarUpload(string arquivo). Sem complicação de heranças, interfaces, etc.

Testes unitários em particular nunca achei necessário, acho um esforço grande para muito pouco retorno em sistemas, onde entregamos funcionalidades e não classes. Para quem fornece frameworks por exemplo é importante.

1 curtida

De novo, compartilho da mesma opinião.
Em todas as empresas em que trabalhei e que era requisito obrigatório o desenvolvimento de testes unitários, 100% dos testes eram “para inglês ver” (pasmem, achei uma classe de testes ontem que só tinha o método e um assertTrue).
Pelo que entendo, a ideia do teste unitário seria garantir que o processamento de uma regra específica seria atendido, de acordo com o que a regra dizia. Tudo o que estivesse fora da regra, geraria resultados errados (exceção, por exemplo).
E a coisa vem como uma bola de neve. O desenvolvedor precisa acelerar o desenvolvimento e sacrifica as boas práticas (como se desenvolver código decente fosse demorado), o mesmo precisa ir mais rápido e faz o teste de qualquer jeito (afinal, o maior desperdício de esforço em um projeto é desenvolvimento e não correção de bugs), aí já está tudo comprometido.
Eu imagino (pois conhecer mesmo, não conheço) que exista algum lugar onde testes são, efetivamente, levados a sério.