As afirmações ou testes de unidade são mais importantes?

50

Ambas as afirmações e testes de unidade servem como documentação para uma base de código e um meio de descobrir bugs. As principais diferenças são que afirmam a função como verificações de integridade e ver entradas reais, enquanto os testes de unidade são executados em entradas simuladas específicas e são testes contra uma única "resposta correta" bem definida. Quais são os méritos relativos de usar testes de afirmações versus testes de unidade como o principal meio de verificar a exatidão? Qual você acredita que deve ser enfatizado com mais força?

    
por dsimcha 11.11.2010 / 05:19
fonte

3 respostas

43

Asserts são úteis para falar sobre o estado interno do programa . Por exemplo, que suas estruturas de dados têm um estado válido, por exemplo, que uma estrutura de dados Time não manterá o valor de 25:61:61 . As condições verificadas por afirmações são:

  • Pré-condições, que garantem que o chamador mantenha seu contrato,

  • Postconditions, que garantem que o candidato mantenha seu contrato e

  • Invariantes, que asseguram que a estrutura de dados sempre mantenha alguma propriedade após a função retornar. Um invariante é uma condição que é uma pré-condição e uma pós-condição.

Testes de unidade são úteis para falar sobre o comportamento externo do módulo . Seu Stack pode ter um estado consistente após o método push() ser chamado, mas se o tamanho da pilha não aumentar em três após ser chamado três vezes, isso é um erro. (Por exemplo, o caso trivial em que a implementação push() incorreta apenas verifica as declarações e saídas.)

Estritamente falando, a principal diferença entre testes de unidade e testes é que os testes de unidade possuem dados de teste (valores para executar o programa), enquanto que as declarações não. Isto é, você pode executar seus testes unitários automaticamente, enquanto você não pode dizer o mesmo para asserções. Por causa desta discussão, eu assumi que você está falando sobre a execução do programa no contexto de testes de função de ordem superior (que executam o programa inteiro e não conduzem módulos como testes de unidade). Se você não está falando sobre testes de função automatizada como o meio de "ver insumos reais", então, claramente, o valor está na automação e, portanto, os testes de unidade ganhariam. Se você está falando sobre isso no contexto de testes de função (automatizados), veja abaixo.

Pode haver alguma sobreposição no que está sendo testado. Por exemplo, uma pós-condição de Stack pode realmente afirmar que o tamanho da pilha aumenta em um. Mas há limites para o que pode ser feito nessa afirmação: ele também deve verificar se o elemento principal é o que acabou de ser adicionado?

Para ambos, o objetivo é aumentar a qualidade. Para testes unitários, o objetivo é encontrar bugs. Para asserções, o objetivo é tornar a depuração mais fácil observando os estados de programa inválidos assim que eles ocorrem.

Note que a técnica nenhuma verifica a correção. Na verdade, se você realizar testes unitários com o objetivo de verificar se o programa está correto, provavelmente você fará testes desinteressantes que você sabe que funcionarão. É um efeito psicológico: você fará o que for para atingir seu objetivo. Se seu objetivo é encontrar bugs, suas atividades refletirão isso.

Ambos são importantes e têm seus próprios propósitos.

[Como nota final sobre asserções: Para obter o máximo de valor, você precisa usá-las em todos os pontos críticos do programa e não em algumas funções importantes. Caso contrário, a fonte original do problema pode ter sido mascarada e difícil de detectar sem horas de depuração.]

    
por 11.11.2010 / 06:06
fonte
7

Ao falar sobre asserções, lembre-se de que elas podem ser desativadas ao apertar um botão.

Exemplo de uma afirmação muito ruim:

char *c = malloc(1024);
assert(c != NULL);

Por que isso é ruim? Porque nenhuma verificação de erro é feita se essa asserção é ignorada devido a algo como NDEBUG sendo definido.

Um teste unitário (provavelmente) apenas segmentaria o código acima. Claro, ele fez o seu trabalho dizendo que algo deu errado, ou fez isso? Qual é a probabilidade de malloc() falhar no teste?

As asserções são para fins de depuração quando um programador precisa sugerir que nenhum evento 'normal' causaria o disparo da declaração. malloc() failing é, na verdade, um evento normal, portanto, nunca deve ser declarado.

Existem muitos outros casos em que as asserções são usadas em vez de lidar adequadamente com coisas que podem dar errado. É por isso que as afirmações obtêm uma má reputação e por que linguagens como o Go não as incluem.

Testes de unidade são projetados para lhe dizer quando algo que você mudou quebrou outra coisa. Eles são projetados para salvar você (na maioria dos casos) de passar por todos os recursos do programa (no entanto, os testadores são importantes para as liberações) toda vez que você faz uma compilação.

Não existe realmente nenhuma correlação distinta entre os dois, além de ambos dizerem que algo deu errado. Pense em uma afirmação como um ponto de interrupção em algo em que você está trabalhando, sem ter que usar um depurador. Pense em um teste de unidade como algo que diz se você quebrou algo em que você não está trabalhando .

    
por 11.11.2010 / 05:45
fonte
5

Ambas são ferramentas usadas para ajudar a melhorar a qualidade geral do sistema que você está construindo. Depende muito da linguagem que você está usando, do tipo de aplicativo que você está criando e de onde seu tempo é mais bem gasto. Sem mencionar que você tem algumas escolas de pensamento sobre isso.

Para começar, se você estiver usando um idioma sem a palavra-chave assert , não será possível usar afirmações (pelo menos não no modo como estamos falando aqui). Por um longo tempo, o Java não tem uma palavra-chave assert , e um número de idiomas ainda não tem. O teste unitário torna-se um pouco mais importante. Em algumas linguagens, as asserções só são executadas quando um sinalizador é definido (novamente com Java aqui). Quando as proteções não estão sempre lá, não é um recurso muito útil.

Existe uma escola de pensamento que diz que se você está "afirmando" alguma coisa, é melhor escrever um bloco de exceção significativo if / throw . Esse processo de pensamento vem de muitas afirmações colocadas no início de um método para garantir que todos os valores estejam dentro dos limites. Testar suas pré-condições é uma parte muito importante de ter uma pós-condição esperada.

Teste de unidade é um código extra que precisa ser escrito e mantido. Para muitos, isso é uma desvantagem. No entanto, com a atual safra de estruturas de teste de unidade lá fora, você pode gerar um número maior de condições de teste com um código comparativamente pequeno. Testes parametrizados e "teorias" irão realizar o mesmo teste com um grande número de amostras de dados que podem descobrir alguns erros difíceis de encontrar.

Pessoalmente acho que ganho mais quilometragem com testes unitários do que com sprinkling, mas isso é devido às plataformas que desenvolvo na maioria das vezes (Java / C #). Outras linguagens têm suporte a asserções mais robustas e até mesmo "Design by Contract" (veja abaixo) para fornecer ainda mais garantias. Se eu estivesse trabalhando com um desses idiomas, poderia usar o DBC mais do que o teste de unidade.

link

    
por 11.11.2010 / 05:51
fonte