Testes estão lá para apoiar e garantir a programação defensiva
A programação defensiva protege a integridade do sistema em tempo de execução.
Os testes são ferramentas de diagnóstico (principalmente estáticas). Em tempo de execução, seus testes não estão à vista. Eles são como andaimes usados para colocar uma parede de tijolos altos ou uma cúpula de rocha. Você não deixa partes importantes fora da estrutura porque você tem um andaime segurando-o durante a construção. Você tem um andaime segurando-o durante a construção para facilitar colocar todas as peças importantes.
EDIT: uma analogia
Que tal uma analogia aos comentários no código?
Os comentários têm o seu propósito, mas podem ser redundantes ou mesmo prejudiciais. Por exemplo, se você colocar conhecimento intrínseco sobre o código nos comentários , em seguida, alterar o código, os comentários se tornarão irrelevantes na melhor das hipóteses e prejudiciais na pior das hipóteses.
Digamos que você coloque muito conhecimento intrínseco de sua base de código nos testes, como o MethodA não pode aceitar um nulo e o argumento do MethodB deve ser > 0
. Então o código muda. Null está bem para A agora, e B pode pegar valores tão pequenos quanto -10. Os testes existentes estão agora funcionalmente errados, mas continuarão a passar.
Sim, você deve atualizar os testes ao mesmo tempo em que atualiza o código. Você também deve atualizar (ou remover) comentários ao mesmo tempo em que atualiza o código. Mas todos sabemos que essas coisas nem sempre acontecem e que erros são cometidos.
Os testes verificam o comportamento do sistema. Esse comportamento real é intrínseco ao próprio sistema, não intrínseco aos testes.
O que poderia dar errado?
O objetivo com relação aos testes é pensar em tudo que poderia dar errado, escrever um teste para ele que verifique o comportamento correto e criar o código de tempo de execução para que ele passe em todos os testes.
O que significa que programação defensiva é o ponto .
TDD impulsiona a programação defensiva, se os testes forem abrangentes.
Mais testes, conduzindo uma programação mais defensiva
Quando erros são encontrados inevitavelmente, mais testes são escritos para modelar as condições que manifestam o erro. Em seguida, o código é corrigido, com código para fazer com que esses testes passem, e os novos testes permanecem no conjunto de testes.
Um bom conjunto de testes vai passar bons e maus argumentos para uma função / método e esperar resultados consistentes. Isso, por sua vez, significa que o componente testado usará verificações de pré-condição (programação defensiva) para confirmar os argumentos passados para ele.
Genericamente falando ...
Por exemplo, se um argumento nulo para um determinado procedimento é inválido, então pelo menos um teste vai passar um nulo, e ele vai esperar uma exceção "argumento nulo inválido" / algum tipo de erro.
Pelo menos um outro teste vai passar um argumento válido , claro - ou passar por um grande array e passar muitos argumentos válidos - e confirmar que o estado resultante é apropriado. / p>
Se um teste não passar esse argumento nulo e for esbofeteado com a exceção esperada (e essa exceção foi lançada porque o código defensivamente verificou o estado passado a ele), então o nulo pode terminar atribuído a uma propriedade de uma classe ou enterrado em uma coleção de algum tipo, onde não deveria ser.
Isso pode causar um comportamento inesperado em alguma parte completamente diferente do sistema para o qual a instância da classe é passada, em algum local geográfico distante após o software ter sido enviado . E esse é o tipo de coisa que estamos tentando evitar, certo?
Pode até ser pior. A instância de classe com o estado inválido pode ser serializada e armazenada, apenas para causar uma falha quando for reconstituída para uso posterior. Nossa, eu não sei, talvez seja um sistema de controle mecânico de algum tipo que não pode reiniciar após um desligamento porque não pode desserializar seu próprio estado de configuração persistente. Ou a instância da classe poderia ser serializada e passada para algum sistema completamente diferente criado por alguma outra entidade, e o sistema pode falhar.
Especialmente se os programadores do outro sistema não codificassem defensivamente.