Sou bastante novo no mundo dos testes de unidade e resolvi adicionar uma cobertura de teste para o meu aplicativo existente esta semana.
Essa é uma tarefa enorme, principalmente por causa do número de classes a serem testadas, mas também porque escrever testes é tudo novo para mim.
Já escrevi testes para várias classes, mas agora estou me perguntando se estou fazendo certo.
Quando estou escrevendo testes para um método, tenho a sensação de reescrever uma segunda vez o que já escrevi no próprio método.
Meus testes parecem tão intimamente ligados ao método (testando todo o caminho de código, esperando que alguns métodos internos sejam chamados várias vezes, com certos argumentos), que parece que, se eu refatorar o método, os testes falharão mesmo que o o comportamento final do método não mudou.
Este é apenas um sentimento e, como já foi dito, não tenho experiência em testes. Se alguns testadores mais experientes do mercado pudessem me dar conselhos sobre como escrever ótimos testes para um aplicativo existente, isso seria muito apreciado.
Edit: Eu adoraria agradecer ao Stack Overflow, tive ótimas contribuições em menos de 15 minutos que responderam mais das horas de leitura on-line que acabei de fazer.
fonte
Respostas:
Eu acho que você está fazendo errado.
Um teste de unidade deve:
Ele não deve olhar dentro do método para ver o que está fazendo; portanto, alterar os internos não deve causar falhas no teste. Você não deve testar diretamente se métodos particulares estão sendo chamados. Se você estiver interessado em descobrir se seu código privado está sendo testado, use uma ferramenta de cobertura de código. Mas não fique obcecado com isso: 100% de cobertura não é um requisito.
Se o seu método chama métodos públicos em outras classes, e essas chamadas são garantidas pela sua interface, você pode testar se essas chamadas estão sendo feitas usando uma estrutura de simulação.
Você não deve usar o próprio método (ou qualquer código interno que ele usa) para gerar o resultado esperado dinamicamente. O resultado esperado deve ser codificado no seu caso de teste para que não seja alterado quando a implementação for alterada. Aqui está um exemplo simplificado do que um teste de unidade deve fazer:
Observe que a forma como o resultado é calculado não é verificada - apenas que o resultado está correto. Continue adicionando cada vez mais casos de teste simples, como o descrito acima, até ter coberto o maior número possível de cenários. Use sua ferramenta de cobertura de código para verificar se você perdeu algum caminho interessante.
fonte
Para testes de unidade, achei o Test Driven (testes primeiro, código segundo) e o código primeiro, teste segundo como extremamente úteis.
Em vez de escrever código, escreva teste. Escreva o código e veja o que você acha que o código deveria estar fazendo. Pense em todos os usos pretendidos e, em seguida, escreva um teste para cada um. Acho os testes de escrita mais rápidos, mas mais envolvidos do que a própria codificação. Os testes devem testar a intenção. Também pense nas intenções de encontrar casos de esquina na fase de redação do teste. E, é claro, ao escrever testes, você pode encontrar um dos poucos usos que causa um bug (algo que muitas vezes encontro, e estou muito feliz que esse bug não corrompeu os dados e não foi verificado).
No entanto, testar é quase como codificar duas vezes. Na verdade, eu tinha aplicativos em que havia mais código de teste (quantidade) do que código de aplicativo. Um exemplo foi uma máquina de estado muito complexa. Eu tinha que ter certeza de que, depois de adicionar mais lógica, a coisa toda sempre funcionava em todos os casos de uso anteriores. E como esses casos eram bastante difíceis de acompanhar, observando o código, acabei tendo um conjunto de testes tão bom para esta máquina que estava confiante de que ela não quebraria mesmo depois de fazer alterações, e os testes salvaram minha bunda algumas vezes . E como os usuários ou testadores estavam encontrando bugs com os casos de fluxo ou de canto inexplicáveis, adivinhem, foram adicionados aos testes e nunca mais aconteceram. Isso realmente deu aos usuários confiança no meu trabalho, além de tornar a coisa toda super estável. E quando teve que ser reescrito por razões de desempenho, adivinhe,
Todos os exemplos simples
function square(number)
são ótimos e provavelmente são maus candidatos para passar muito tempo testando. Aqueles que fazem lógica comercial importante, é aí que os testes são importantes. Teste os requisitos. Não basta testar o encanamento. Se os requisitos mudarem, adivinhem, os testes também devem.O teste não deve estar literalmente testando se a função invocou a barra de função 3 vezes. Isso esta errado. Verifique se o resultado e os efeitos colaterais estão corretos, não a mecânica interna.
fonte
Vale a pena notar que os testes de unidade de adaptação automática no código existente são muito mais difíceis do que conduzir a criação desse código com testes em primeiro lugar. Essa é uma das grandes questões ao lidar com aplicativos herdados ... como fazer o teste de unidade? Isso já foi solicitado muitas vezes antes (para que você possa ser fechado como uma pergunta idiota), e as pessoas geralmente acabam aqui:
Movendo o código existente para o Test Driven Development
Eu apóio a recomendação do livro da resposta aceita, mas além disso há mais informações vinculadas nas respostas.
fonte
Não escreva testes para obter uma cobertura completa do seu código. Escreva testes que garantam seus requisitos. Você pode descobrir caminhos de código desnecessários. Por outro lado, se necessário, eles estão lá para atender a algum tipo de requisito; encontre o que é e teste o requisito (não o caminho).
Mantenha seus testes pequenos: um teste por requisito.
Mais tarde, quando precisar fazer uma alteração (ou escrever um novo código), tente escrever um teste primeiro. Apenas um. Depois, você terá dado o primeiro passo no desenvolvimento orientado a testes.
fonte
O teste de unidade é sobre a saída que você obtém de uma função / método / aplicativo. Não importa como o resultado é produzido, apenas importa que esteja correto. Portanto, sua abordagem de contagem de chamadas para métodos internos está errada. O que costumo fazer é sentar e escrever o que um método deve retornar, com base em certos valores de entrada ou em um determinado ambiente e, em seguida, escrever um teste que compare o valor real retornado com o que eu criei.
fonte
Tente escrever um teste de unidade antes de escrever o método a ser testado.
Definitivamente, isso forçará você a pensar um pouco diferente sobre como as coisas estão sendo feitas. Você não tem idéia de como o método vai funcionar, exatamente o que ele deve fazer.
Você sempre deve testar os resultados do método, não como o método obtém esses resultados.
fonte
os testes devem melhorar a manutenção. Se você alterar um método e um teste for interrompido, isso pode ser uma coisa boa. Por outro lado, se você olhar para o seu método como uma caixa preta, não deve importar o que está dentro do método. O fato é que você precisa zombar de alguns testes e, nesses casos, você realmente não pode tratar o método como uma caixa preta. A única coisa que você pode fazer é escrever um teste de integração - você carrega uma instância totalmente instanciada do serviço em teste e faz com que ele faça o mesmo que faria em seu aplicativo. Então você pode tratá-lo como uma caixa preta.
Isso ocorre porque você está escrevendo seus testes depois de escrever seu código. Se você fizesse o contrário (escrevesse os testes primeiro), não seria assim.
fonte