A doce ilusão da alta cobertura em testes
August 28th, 2008 • IN PORTUGUESE
Há tempos atrás - quando eu trabalhava em grandes empresas de desenvolvimento de software - tinhamos em nosso workflow a prática de enviar nossos códigos para a equipe de QA realizar uma “extensa” bateria de testes. O código que passava nos testes era considerado de qualidade. Para os que não passavam, os programadores responsáveis eram alertados sobre a vulnerabilidade e tinham a missão de corrigí-los.
Lembro-me de participar de reuniões por motivos de falha encontrada no sistema após a entrada em produção. Os motivos de existir a falha eram estudados e na grande maioria das vezes, os testers acabavam sendo apontados como culpados por “não terem realizados testes suficientes”. Não me lembro de presenciar nada sobre “fazer testes ruins”, testes que em si, pouco testavam.
Após a expansão de TDD e dos frameworks que permitiram automatizar testes, medir a cobertura em testes de um determinado projeto foi uma evolução natural para quem utiliza testes automatizados. Diversas ferramentas, para diversas linguagens/plataformas, começaram a aparecer e a facilitar a monitoração deste indicador.
Porém, tenho visto em alguns projetos, que o índice de cobertura em testes tem sido encarado como um fim, ao invés de uma consequência. Valores para o índice de cobertura em testes tem sido estipulados no início do projeto, e o código - consequentemente a equipe - passa a ser encarada como “de qualidade”, caso mantenha o valor inicialmente estipulado.
Coincidentemente - ou não - alguns códigos de testes desses projetos não são tão bem escritos. Testam pouca coisa, ou absolutamente nada. Mas mesmo assim, a avaliação que o código recebe é de “auto grau de qualidade”, já que apresenta um elevado índice de cobertura em testes.
Resolvi então, escrever alguns exemplos para ilustrar esse post. No trecho abaixo, temos uma classe NumberUtil, que possui 2 métodos: 1) humanize_array - onde dado um array é retornado um outro array, porém com os números em forma de string - por extenso; 2) humanize - onde dado um número retorna a sua string - por extenso;
Olhando atentamente ao trecho acima, percebemos que existe um bug na linha 10. Isso faz com que dado o número 1, a string retornada seja “zero”.
Logo em seguida temos a sua classe de teste, chamada NumberUtilTest. Esperamos que essa classe possua uma boa cobertura em testes para que possa identificar o problema no trecho anterior.
Os dois métodos de NumberUtil são testados, a cobertura está boa, mas se executarmos os testes, nenhuma falha é apresentada. Exatamente o contrário do que imaginávamos, já que existe um bug na classe.
Isso acontece porque o método test_should_humanize_array não está bem escrito. Existe uma falha básica nele, que faz com que não identifique o bug. Ele deve esperar o valor “one” ao invés de “zero”, já que a primeira posição do array dado, tem valor 1.
Temos aqui um bom exemplo de alta cobertura em testes, mas com pouca qualidade em código, já que os testes não identificam um blug relativamente simples.
Outros tipos de falha em sistema, ocorrem por falta de implementação, falta de código. Isso é realmente mais difícil de ser capturado em testes automatizados. Vamos dar uma olhada no trecho a seguir:
Acima temos uma classe chamada Person, onde o programador esqueceu de retirar o comentário em uma validação para o atributo username. Isso faz com que, mesmo que nenhum valor para username seja informado, o objeto será salvo no banco de dados.
Vamos então verificar a sua classe de teste, chamada PersonTest.
Podemos verificar que o método test_should_create_person realiza um teste simples para a criação de um objeto Person, sem informar valor para o atributo username. Esse teste será executado com sucesso, mesmo que não seja desejável ter um objeto Person sem username preenchido.
Para isso, o mais interessante seria termos uma classe de teste um pouco mais robusta, conforme podemos visualizar abaixo:
Acima, testes são realizados para identificar falhas em código existente e em códigos não existente.
Com base nesses exemplos simples, podemos verificar que ter um alto índice em cobertura em testes não é razão para ter um código de qualidade escrito.
Fixar valores para esse indicador, não fará com que código de qualidade seja escrito. É preferencial que a escrita do bom código seja estimulada, assim como bons testes. Com isso, a alta cobertura em testes se apresentará como consequência e não como fim.
Tenho certeza que você já se deparou com código de teste mal escrito. Código que no final das contas, nada ou pouco testava. Caso tenha esse exemplo, mande para mim
Envie para o meu e-mail (fmesquitacunha @ gmail), cole o código no Gist ou Pastie e me mande a URL. Ou ainda, faça um post no seu blog e link para cá. Quem sabe consigo fazer uma série sobre *Testes que nada testam* ![]()

Muito bom o post!
Cara, e aquele que o cara executa uma consulta de fornecedor, por exemplo, pega a coleção de retorno e apenas testa se a esta não é nula e se tem algum item. Mas não testa se os tais itens, de fato, são fornecedores e se seus dados estão corretamente preenchidos. Ai, o que acontece? Já dá pra imaginar, né? Bomba!