Quão defensivos devemos ser?

11

Temos rodado o Pex sobre algum código, e ele tem mostrado algumas coisas boas (coisas ruins, mas mostradas antes da produção!).

No entanto, uma das coisas boas do Pex é que ele não para necessariamente de tentar encontrar problemas.

Uma área que descobrimos é que, ao passar uma string, não estávamos verificando se havia strings vazias.

Então nós mudamos:

if (inputString == null)

para

if (string.IsNullOrEmpty(inputString)) // ***

Isso corrigiu os problemas iniciais. Mas então, quando rodamos o Pex novamente, ele decidiu que:

inputString = "\0";

estava causando problemas. E depois

inputString = "\u0001";

O que decidimos é que os padrões podem ser usados ​​se encontrarmos // ***e estamos felizes vendo a exceção causada por qualquer outra entrada estranha (e lidando com ela).

Isso é suficiente?

Peter K.
fonte
Você verificou se consegue desativar alguns dos avisos? Eu sei que o JTest faria isso também, mas também foi uma opção para desativar algumas de suas recomendações. Demorou algum tempo ajustando as configurações para obter um perfil de teste de código que gostamos.
FrustratedWithFormsDesigner
@ Frustrated: Sim, certamente existem ajustes no perfil que podemos fazer e estamos fazendo. Pex é uma ótima ferramenta, a propósito. Os resultados apresentados apenas levaram à pergunta.
Peter K.
Não tenho uma resposta para você, mas queria agradecer por esse link para essa coisa de Pex; parece bem legal se é o que eu acho que é e criará automaticamente (?) testes de unidade para o código existente. O código no qual estou trabalhando é enorme e não possui testes e códigos muito bem acoplados, por isso é impossível refatorar qualquer coisa.
Wayne Molina
@WayneM: Sim, é muito bom, embora eu precisasse passar por alguns exemplos para entender o que estava fazendo. Para código legado, é ótimo. É ainda melhor para o novo código!
Peter K.

Respostas:

9

Três perguntas devem ajudá-lo a determinar o quão defensivo deve ser na sua codificação.

Primeiro - Quais são as consequências de uma entrada ruim de entrada? Se for uma mensagem de erro em um dos PCs do desenvolvedor, talvez não seja tão crítico para ficar na defensiva. Isso poderia resultar em interrupções financeiras nos clientes, interrupção das informações contábeis do IE? É um sistema em tempo real onde vidas estão em risco? No cenário de vida / morte, provavelmente deve haver mais códigos de validação e tratamento de erros do que o código de recurso real.

Em segundo lugar, quantas pessoas reutilizarão essa função ou parte do código? Só você? O seu departamento? Sua empresa? Seus clientes? Quanto mais amplo o uso do código, mais defensivo.

Terceiro - qual é a fonte da entrada que estou validando? Se for uma entrada do usuário ou de um site público, eu ficaria super defensivo. Se a entrada sempre vier do seu código, seja um pouco defensivo, mas não gaste tempo indevido colocando cheques.

Sempre será possível adicionar mais verificação e validação de erros em um sistema. O ponto é que o custo de escrever e manter esse código supera o custo dos problemas causados ​​por erros no código.

Bork Blatt
fonte
6

Os usuários são maus, e tudo o que inserem deve ser verificado com o máximo rigor.

Qualquer coisa gerada sem o benefício da entrada do usuário ou de dados pré-higienizados não deve precisar ser verificada no mesmo nível. O problema aqui é quando você esquece e usa esses métodos em dados incorretos, porque você esqueceu que o código não foi reforçado.

A única coisa que você deve sempre verificar é qualquer coisa que possa causar um estouro ou uma falha. Não importa quão profundamente esse método esteja enterrado, e como você tem certeza de que essa condição nunca poderia ocorrer. Você precisa programar para isso de qualquer maneira, apenas para aplacar Murphy.

Satanicpuppy
fonte
2

Eu ficaria tão defensivo quanto você precisa. Um pouco ambíguo, acho que sim, mas tentarei explicar.

Quando você corrige um método, se esse método possui parâmetros de entrada, é necessário tomar uma decisão sobre o que você espera que esses parâmetros sejam. Em situações e lugares dentro de um aplicativo, isso será diferente. Por exemplo, se um método ou parte do código estiver aceitando dados de uma entrada do usuário, você deverá cobrir toda a base do código e manipular qualquer entrada adequadamente, seja por meio de uma mensagem de erro ou de uma maneira agradável de exibir dados inaceitáveis.

Se o método for uma API exposta, é o mesmo. Você não pode controlar o que está chegando, portanto, tente cobrir todos os aspectos e programar adequadamente.

Para métodos produzidos no mecanismo principal do seu projeto, aqui você tem que tomar uma decisão. Suponho que os dados que estão chegando foram pré-selecionados e validados antes de chegarem ou devo fazer as verificações necessárias. Eu acho que isso depende do nível conceitual do método e se esse é um local aceitável para verificar. Então, as coisas que posso considerar é:

1) Esse é o único local em que preciso fazer essa verificação? Essa variável precisará ser verificada em vários locais diferentes para essa condição. Nesse caso, posso fazer a verificação uma vez mais acima na cadeia e assumir a validade depois

2) Outros componentes do sistema devem funcionar com os métodos e interfaces que escrevo. Nesse caso, você pode controlar através de instruções de declaração de depuração, exceções de depuração, comentários de método e arquitetura geral do sistema o resultado que você precisa ou os dados precisam de verificações.

3) Quais são os resultados da falha neste momento do código. Isso fará com que a coisa toda falhe? Algum erro será detectado em outro lugar e será, pelo menos, detectado.

4) Faz sentido colocar um cheque aqui? Às vezes, verificar um dado possível de dados corrompidos, embora ajude a resolver o problema naquele momento e não a erros, pode ajudar a encobri-lo. Nesse ponto, você pode passar horas perseguindo um problema diferente apenas para descobrir que o problema real foi devido a uma verificação válida dos dados que estão na cadeia de eventos que estavam em cascata com a que o usuário / desenvolvedor foi relatado.

Em geral, sou um programador defensivo, no entanto, também acredito que, com o TDD completo e os testes de unidade apropriados, você pode fazer as verificações no código nos níveis exigidos e ter certeza de que está funcionando como deveria quando chega a qualquer seção de nível inferior .

dreza
fonte
1

Passei semanas antes em erros que poderiam ter sido detectados com 5 minutos de trabalho extra como esse na frente. Na minha opinião, esse trabalho inicial sempre vale a pena. Você estará corrigindo o bug eventualmente. A única questão é quanto tempo você levará.

Uma coisa que esses tipos de ferramentas de análise geralmente descobrem são coisas que não são necessariamente erros, mas são maus hábitos de programação que tornam mais provável a ocorrência de erros. Um desses hábitos comuns é a inicialização variável. Às vezes, uma string vazia é um valor perfeitamente válido para uma variável. Nesse caso, você deseja configurar sua ferramenta para não considerar um erro nessa instância específica. No entanto, geralmente uma cadeia vazia não é um valor válido para uma variável, mas as pessoas a definem de qualquer maneira, porque o compilador reclama se não há algo lá ou o seu idioma é automaticamente inicializado para a cadeia vazia, válido ou não.

Isso frustra as pessoas porque parece um problema 22 sem solução válida, mas a solução é refatorar seu código para que não seja necessário inicializar a variável até que exista um valor válido a ser inserido. Isso requer uma reflexão extra antecipada, mas torna seu código muito mais robusto e é realmente mais fácil de escrever e manter a longo prazo.

Karl Bielefeldt
fonte