Eu tenho uma implementação esquelética, como no Item 18 do Java Efetivo (discussão estendida aqui ). É uma classe abstrata que fornece 2 métodos públicos methodA () e methodB () que chamam subclasses de métodos para "preencher as lacunas" que não posso definir de maneira abstrata.
Eu o desenvolvi primeiro criando uma classe concreta e escrevendo testes de unidade para ele. Quando a segunda classe chegou, eu pude extrair o comportamento comum, implementar as "lacunas ausentes" na segunda classe e ela estava pronta (é claro, os testes de unidade foram criados para a segunda subclasse).
O tempo passou e agora tenho 4 subclasses, cada uma implementando 3 métodos curtos e protegidos que são muito específicos para sua implementação concreta, enquanto a implementação esquelética faz todo o trabalho genérico.
Meu problema é que, quando crio uma nova implementação, escrevo os testes novamente:
- A subclasse chama um determinado método necessário?
- Um determinado método possui uma anotação?
- Eu obtenho os resultados esperados do método A?
- Eu obtenho os resultados esperados do método B?
Embora veja vantagens com essa abordagem:
- Ele documenta os requisitos da subclasse através de testes
- Pode falhar rapidamente em caso de refatoração problemática
- Teste a subclasse como um todo e por meio de sua API pública ("methodA () funciona?" E não "este método protegido funciona?")
O problema que tenho é que os testes para uma nova subclasse são basicamente simples: - Os testes são todos iguais em todas as subclasses, eles apenas alteram o tipo de retorno dos métodos (a implementação do esqueleto é genérica) e as propriedades que eu verifique a parte afirmativa do teste. - Normalmente, as subclasses não têm testes específicos para elas
Gosto da maneira como os testes se concentram no resultado da subclasse e a protegem da refatoração, mas o fato de implementar o teste se tornar apenas "trabalho manual" me faz pensar que estou fazendo algo errado.
Esse problema é comum ao testar a hierarquia de classes? Como posso evitar isso?
ps: Pensei em testar a classe esqueleto, mas também parece estranho criar um mock de uma classe abstrata para poder testá-la. E acho que qualquer refatoração na classe abstrata que mude o comportamento que não é esperado pela subclasse não seria percebida tão rapidamente. Minha coragem me diz que testar a subclasse "como um todo" é a abordagem preferida aqui, mas fique à vontade para me dizer que estou errado :)
ps2: pesquisei no Google e encontrei muitas perguntas sobre o assunto. Um deles é esse que tem uma ótima resposta de Nigel Thorne, meu caso seria o "número 1" dele. Isso seria ótimo, mas não posso refatorar neste momento, então teria que conviver com esse problema. Se eu pudesse refatorar, teria cada subclasse como estratégia. Tudo bem, mas eu ainda testaria a "classe principal" e as estratégias, mas não notaria nenhuma refatoração que rompa a integração entre a classe principal e as estratégias.
ps3: Encontrei algumas respostas dizendo que "é aceitável" testar a classe abstrata. Concordo que isso é aceitável, mas gostaria de saber qual é a abordagem preferida neste caso (ainda estou começando com testes de unidade)