Eu pensei em adicionar mais quatro casos, em que Debug.Assert pode ser a escolha certa.
1) Algo que não vi mencionado aqui é a cobertura conceitual adicional que o Asserts pode fornecer durante o teste automatizado . Como um exemplo simples:
Quando algum chamador de nível superior é modificado por um autor que acredita ter expandido o escopo do código para lidar com cenários adicionais, idealmente (!) Eles escreverão testes de unidade para cobrir essa nova condição. Pode ser que o código totalmente integrado pareça funcionar bem.
No entanto, na verdade, uma falha sutil foi introduzida, mas não detectada nos resultados do teste. O receptor tornou-se não-determinístico, neste caso, e só acontece para fornecer o resultado esperado. Ou talvez tenha gerado um erro de arredondamento despercebido. Ou causou um erro que foi compensado igualmente em outro lugar. Ou concedido não apenas o acesso solicitado, mas privilégios adicionais que não devem ser concedidos. Etc.
Nesse momento, as instruções Debug.Assert () contidas no receptor juntamente com o novo caso (ou caso de borda) acionado por testes de unidade podem fornecer uma notificação inestimável durante o teste de que as suposições do autor original foram invalidadas e o código não deve ser lançado sem revisão adicional. Afirmações com testes de unidade são os parceiros perfeitos.
2) Além disso, alguns testes são simples de escrever, mas de alto custo e desnecessários, dadas as premissas iniciais . Por exemplo:
Se um objeto puder ser acessado apenas a partir de um determinado ponto de entrada seguro, uma consulta adicional deve ser feita no banco de dados de direitos de rede de cada método de objeto para garantir que o chamador tenha permissões? Certamente não. Talvez a solução ideal inclua armazenamento em cache ou alguma outra expansão de recursos, mas o design não exige isso. Um Debug.Assert () mostrará imediatamente quando o objeto foi anexado a um ponto de entrada não seguro.
3) Em seguida, em alguns casos, seu produto pode não ter uma interação de diagnóstico útil para todas ou parte de suas operações quando implantado no modo de liberação . Por exemplo:
Suponha que seja um dispositivo incorporado em tempo real. Lançar exceções e reiniciar quando encontrar um pacote mal formado é contraproducente. Em vez disso, o dispositivo pode se beneficiar da operação com o melhor esforço, até o ponto de renderizar ruído em sua saída. Ele também pode não ter uma interface humana, um dispositivo de registro ou até mesmo ser fisicamente acessível por humanos quando implantado no modo de liberação, e a conscientização dos erros é melhor fornecida pela avaliação da mesma saída. Nesse caso, asserções liberais e testes completos de pré-lançamento são mais valiosos que exceções.
4) Por fim, alguns testes não são necessários apenas porque o chamado é percebido como extremamente confiável . Na maioria dos casos, quanto mais reutilizável for o código, mais esforço será feito para torná-lo confiável. Portanto, é comum a exceção para parâmetros inesperados de chamadores, mas afirmar para resultados inesperados de callees. Por exemplo:
Se uma String.Find
operação principal declarar que retornará a -1
quando o critério de pesquisa não for encontrado, você poderá executar com segurança uma operação em vez de três. No entanto, se ele realmente retornou -2
, você pode não ter um curso de ação razoável. Seria inútil substituir o cálculo mais simples por outro que testa separadamente um -1
valor e não é razoável na maioria dos ambientes de lançamento para agrupar seu código com testes, garantindo que as principais bibliotecas estejam operando conforme o esperado. Nesse caso, as afirmações são ideais.