Existem testes para apoiar e garantir uma 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. São como andaimes usados para erguer uma parede alta de tijolos ou uma cúpula de pedra. Você não deixa peças importantes fora da estrutura porque possui um andaime segurando-o durante a construção. Você tem um andaime segurando-o durante a construção para facilitar a colocação de todas as peças importantes.
EDIT: Uma analogia
Que tal uma analogia com os comentários no código?
Os comentários têm seu objetivo, mas podem ser redundantes ou até prejudiciais. Por exemplo, se você colocar conhecimentos intrínsecos sobre o código nos comentários , altere o código, os comentários se tornarão irrelevantes na melhor das hipóteses e prejudiciais na pior.
Então, digamos que você coloque muito conhecimento intrínseco da sua base de código nos testes, como o Método A não pode aceitar um nulo e o argumento do Método B deve ser > 0
. Então o código muda. Nulo é aceitável para A agora e B pode assumir valores tão pequenos quanto -10. Os testes existentes agora estão funcionalmente errados, mas continuarão a ser aprovados.
Sim, você deve atualizar os testes ao mesmo tempo em que atualiza o código. Você também deve atualizar (ou remover) os 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 dos testes é pensar em tudo o que pode dar errado, escrever um teste para verificar 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 a programação defensiva é o ponto .
O TDD aciona a programação defensiva, se os testes forem abrangentes.
Mais testes, gerando uma programação mais defensiva
Quando erros são inevitavelmente encontrados, mais testes são escritos para modelar as condições que manifestam o erro. Em seguida, o código é corrigido, com o código para fazer esses testes passarem, e os novos testes permanecem no conjunto de testes.
Um bom conjunto de testes passa os argumentos bons e ruins para uma função / método e espera resultados consistentes. Por sua vez, isso 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 procedimento específico é inválido, pelo menos um teste passa em nulo e espera uma exceção / erro "argumento nulo inválido" de algum tipo.
Pelo menos um outro teste passa um argumento válido , é claro - ou passa por uma grande matriz e passa por vários argumentos válidos - e confirma que o estado resultante é apropriado.
Se um teste não passa nesse argumento nulo e recebe um tapa com a exceção esperada (e essa exceção foi lançada porque o código verificou defensivamente o estado passado a ele), o nulo pode acabar atribuído à propriedade de uma classe ou enterrado em uma coleção de algum tipo onde não deveria estar.
Isso pode causar comportamento inesperado em alguma parte totalmente diferente do sistema para a qual a instância da classe é passada, em algum local geográfico distante após o envio do software . E esse é o tipo de coisa que realmente estamos tentando evitar, certo?
Pode até ser pior. A instância da 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 possa reiniciar após um desligamento, porque não pode desserializar seu próprio estado de configuração persistente. Ou a instância da classe pode ser serializada e passada para um sistema totalmente diferente criado por outra entidade, e esse sistema pode falhar.
Especialmente se os programadores do outro sistema não codificassem defensivamente.