Suponha que eu tenha um gerente de classe derivado de uma classe base Employee e que Employee tenha um método getEmail () herdado pelo Manager . Devo testar se o comportamento do método getEmail () de um gerente é de fato o mesmo que o de um funcionário?
No momento em que esses testes são escritos, o comportamento será o mesmo, mas é claro que em algum momento no futuro alguém poderá substituir esse método, alterar seu comportamento e, portanto, interromper meu aplicativo. No entanto, parece um pouco estranho testar essencialmente a ausência de código de intromissão.
(Observe que o método Manager :: getEmail () de teste não melhora a cobertura do código (ou qualquer outra métrica de qualidade do código (?)) Até que o Manager :: getEmail () seja criado / substituído.)
(Se a resposta for "Sim", algumas informações sobre como gerenciar testes compartilhados entre as classes base e derivadas serão úteis.)
Uma formulação equivalente da pergunta:
Se uma classe derivada herda um método de uma classe base, como você expressa (teste) se espera que o método herdado:
- Comporte-se exatamente da mesma maneira que a base agora (se o comportamento da base mudar, o comportamento do método derivado não);
- Comporte-se exatamente da mesma maneira que a base para todos os tempos (se o comportamento da classe base mudar, o comportamento da classe derivada também mudará); ou
- Comporte-se como quiser (você não se importa com o comportamento desse método porque nunca o chama).
fonte
Manager
classeEmployee
foi o primeiro grande erro.Respostas:
Eu adotaria a abordagem pragmática aqui: se alguém, no futuro, substituir o Manager :: getMail, será responsabilidade do desenvolvedor fornecer código de teste para o novo método.
Claro que isso só é válido se
Manager::getEmail
realmente tiver o mesmo caminho de código queEmployee::getEmail
! Mesmo que o método não seja substituído, ele pode se comportar de maneira diferente:Employee::getEmail
poderia chamar algum virtual protegidogetInternalEmail
que é substituídoManager
.Employee::getEmail
poderia acessar algum estado interno (por exemplo, algum campo_email
), que pode diferir nas duas implementações: Por exemplo, a implementação padrão deEmployee
poderia garantir isso_email
sempre[email protected]
, masManager
é mais flexível na atribuição de endereços de email.Nesses casos, é possível que um erro se manifeste apenas em
Manager::getEmail
, mesmo que a implementação do método em si seja a mesma. Nesse caso, testarManager::getEmail
separadamente pode fazer sentido.fonte
Eu gostaria.
Se você está pensando "bem, é realmente apenas chamando Employee :: getEmail () porque eu não o substitui, então não preciso testar o Manager :: getEmail ()", então você não está realmente testando o comportamento do Manager :: getEmail ().
Eu pensaria no que apenas o Manager :: getEmail () deve fazer, não se é herdado ou substituído. Se o comportamento de Manager :: getEmail () for retornar o que Employee :: getMail () retornar, esse é o teste. Se o comportamento é retornar "[email protected]", esse é o teste. Não importa se é implementado por herança ou substituído.
O que importa é que, se ele mudar no futuro, seu teste o capturará e você saberá que algo foi quebrado ou precisa ser reconsiderado.
Algumas pessoas podem discordar da aparente redundância na verificação, mas o meu contrário é que você está testando o comportamento Employee :: getMail () e Manager :: getMail () como métodos distintos, sejam eles herdados ou não ou substituído. Se um desenvolvedor futuro precisar alterar o comportamento do Manager :: getMail (), também precisará atualizar os testes.
As opiniões podem variar, acho que Digger e Heinzi deram justificativas razoáveis para o contrário.
fonte
Não teste a unidade. Teste funcional / de aceitação.
Os testes de unidade devem testar todas as implementações, se você não estiver fornecendo uma nova implementação, siga o princípio DRY. Se você quiser gastar algum esforço aqui, pode aprimorar o teste de unidade original. Somente se você substituir o método, deverá escrever um teste de unidade.
Ao mesmo tempo, os testes funcionais / de aceitação devem garantir que, no final do dia, todo o seu código faça o que é suposto e, esperançosamente, capte qualquer estranheza da herança.
fonte
As regras de Robert Martin para TDD são:
Se você seguir essas regras, não poderá entrar em uma posição para fazer essa pergunta. Se o
getEmail
método precisar ser testado, ele teria sido testado.fonte
Sim, você deve testar os métodos herdados, pois no futuro eles poderão ser substituídos. Além disso, os métodos herdados podem chamar métodos virtuais substituídos , o que alteraria o comportamento do método herdado não substituído.
A maneira como eu testo isso é criando uma classe abstrata para testar a classe base ou a interface (possivelmente abstrata), desta forma (em C # usando NUnit):
Então, eu tenho uma classe com testes específicos para o
Manager
e integro os testes do funcionário como este:A razão pela qual posso fazê-lo é o princípio de substituição de Liskov: os testes de
Employee
ainda devem passar quando umManager
objeto é passado, uma vez que deriva deleEmployee
. Portanto, preciso escrever meus testes apenas uma vez e verificar se eles funcionam para todas as implementações possíveis da interface ou da classe base.fonte
setUp()
próprio método do xUnit ! E praticamente toda estrutura de MVC da Web que envolve a substituição de um método "index" também quebra o LSP, que é basicamente todos eles.class ManagerTests : ExployeeTests
? (isto é, espelhando a herança das classes em teste.) É principalmente estética, para ajudar na visualização dos resultados?Eu diria que não, pois, na minha opinião, seria um teste repetido que eu testaria uma vez nos testes com funcionários e seria isso.
Se o método for substituído, você precisará de novos testes para verificar o comportamento substituído. Esse é o trabalho da pessoa que implementa o
getEmail()
método de substituição .fonte
Não, você não precisa testar métodos herdados. As classes e seus casos de teste que dependem desse método serão interrompidos de qualquer maneira se o comportamento for alterado
Manager
.Pense no seguinte cenário: O endereço de email é montado como [email protected]:
Você testou a unidade e funciona bem para o seu
Employee
. Você também criou uma classeManager
:Agora você tem uma classe
ManagerSort
que classifica os gerentes em uma lista com base no endereço de email. De sua parte, você supõe que a geração de e-mail seja a mesma que emEmployee
:Você escreve um teste para o seu
ManagerSort
:Tudo funciona bem. Agora alguém vem e substitui o
getEmail()
método:Agora o que acontece? Sua
testManagerSort()
falha falhará porque ogetEmail()
deManager
foi substituído. Você investigará esse problema e encontrará a causa. E tudo sem escrever uma caixa de teste separada para o método herdado.Portanto, você não precisa testar métodos herdados.
Caso contrário, por exemplo, em Java, você teria que testar todos os métodos herdados de
Object
liketoString()
,equals()
etc. em todas as classes.fonte