Testes Unitários: por que escrever?

Introdução

Como podemos saber se um software não possui problemas? A resposta é bastante simples: temos de testá-lo para termos uma garantia – mesmo que mínima – de que não temos problemas. Podemos testar o nosso software de diversas formas: por meio de testes de aceitação, testes de carga (stress), testes de integração ou utilizando testes unitários.

O teste unitário é aquele implementado pelo desenvolvedor. Segundo Vicent Massol, autor do livro JUnit in Action,

Um teste unitário examina o comportamento de uma unidade distinta de trabalho.

Nesta definição, dois termos merecem ser destacados: comportamento e unidades de trabalho.

Um teste unitário deve ser capaz de examinar o comportamento do código sob as mais variadas condições. Ou seja, como o código deve se comportar se determinado parâmetro for passado (ou não), o que ele retorna se determinada condição for verdadeira, os efeitos colaterais que ele causa durante a sua execução, se determinada exceção é lançada etc.

Testes unitários, como o nome sugere, devem testar unidades de trabalho isoladas. E geralmente, em termos de códigos orientados a objeto, estas unidades de trabalho são métodos de uma classe. Quando eu digo isoladas, é realmente isoladas! Um teste unitário, tipicamente, testa aquele e somente aquele método, evitando acesso à outros recursos como sistema de arquivos, banco de dados, rede etc. Para testar métodos que fazem uso desses recursos em suas implementações, devemos fazer uso de stubs e/ou mocks.

Vantagens dos Testes Unitários

Algumas pessoas ainda se perguntam o porquê de se escrever teste unitários. Eu já tive um gerente que acreditava que escrever testes era perda de tempo. A crença dele se baseava no fato de que ao se escrever testes unitários estamos gastando mais tempo para entregar o sistema e gerando mais código para manutenção. Ele não estava de todo errado. No entanto os benefícios gerados pelo uso de testes unitários suplantam em muito essas “supostas” desvantagens. Abaixo segue uma lista curta de vantagens que existem ao se fazer testes unitários.

  • Testes unitários permitem maior cobertura de teste

O uso de teste unitários permite que testemos uma porção muito maior do código do sistema do que àquele feito manualmente. É muito mais fácil exercitar todos os caminhos possíveis por meio de testes unitários do que em testes manuais. É mais simples simular determinadas condições que, nos testes manuais, pode ser muito difícil – senão impossível – de serem replicadas. E isso apenas alterando os parâmetros de um método ou a configuração de uma classe.

  • Testes unitários previnem regressão

Quantas vezes, após o lançamento de uma nova versão de um software, um erro aparece e você se depara perguntando: “Como pode? Eu nem toquei no código dessa tela!”. Quando utilizamos testes unitários automatizados de forma consistente, possuindo uma boa suite de casos de teste, essa situação é cada vez mais rara. Isso porque temos a garantia de que a introdução de novos códigos não fazem com que outras funcionalidades deixem de funcionar corretamente, pois, a execução dos testes unitários dessas outras vão nos alertar se elas “quebrarem”. Ou seja, os testes unitários nos avisam se o software, com a introdução de novas funcionalidades, regrediu ou não.

  • Testes unitários incentivam o refactoring

O processo de refactoring deve ser uma constante na atividade de desenvolvimento de software. Sempre que nos deparamos com um código de má qualidade, deve ser nossa obrigação refatorar aquele código – seja introduzindo um novo método, quebrando em classes, otimizando o código etc. No entanto, muitas vezes, temos medo (isso mesmo, medo!) de mexer naquele código tenebroso, pois, não sabemos direito o que ele faz e qual a conseqüência de modificar aquela implementação. Isto é, temos medo da regressão.

No entanto, se pensarmos no processo de refactoring, isso não deveria ser um problema. Afinal refatorar nada mais é do que dar uma nova estrutura ao código sem afetar seu comportamento, isto é, mudamos a “cara” dele, mas não mudamos o que ele faz. O problema é que, sem testes unitários, não temos a mínima garantia de que aquele código funciona. Não temos como comparar se o refactoring que fizemos “quebrou” ou não aquela parte do código. Os testes unitários nos fornecem essa garantia: o código, mesmo após as nossas mudanças, tem de estar funcionando. Se isso não for verdade, o teste nos avisará. Isso permite melhorar a qualidade de nosso código de forma contínua.

  • Testes unitários evitam longas sessões de debug

Quantas vezes você se deparou com um erro e, para confirmar sua existência, iniciou uma sessão de debug? Imagino que algumas vezes, não é? Eu mesmo posso dizer que fiz isso inúmeras vezes: ir executando o código da aplicação linha por linha, inspecionando as variáveis em busca do erro etc.

Não sei quanto a você, mas para mim esse é um processo cansativo. Com os testes unitários essas longas – e cansativas – sessões não são mais necessárias. Quando surgir um erro, em vez de ter que executar a aplicação em modo debug e sair em busca do problema, devemos escrever um teste de unidade e verificar se o problema realmente existe. Com isso, além de ganhar tempo ao evitar o processo de debug, aumentamos nossa suite de testes e diminuímos a possibilidade de o software sofrer regressão.

  • Testes de unidade servem como documentação

É isso mesmo. Você leu certo: d-o-c-u-m-e-n-t-a-ç-ã-o. Os teste unitários, quando bem escritos, servem como uma forma de documentação das funcionalidades do sistema. Os testes exercitam o comportamento do sistema, verificando como ele deve funcionar sob determinadas situações. Ao ler os casos de teste, novos desenvolvedores são capazes de entender como o sistema deve se comportar. Eu diria que o teste de unidade é um tipo de especificação executável e sempre atualizada.

Pare para pensar sobre o que você prefere:

  1. ler um manual/documento com 20 páginas sobre as funcionalidades do sistema (ou o uso de uma API) OU
  2. ver exemplos práticos de código de uso dessas funcionalidades/API?

Eu fico, sem dúvidas, com a segunda opção. E é isso que os testes são: exemplos práticos de uso do sistema.

Conclusão

Acho que essa pequena lista mostra as vantagens de se escrever testes unitários. Eu sou suspeito para falar, pois, sou um grande defensor dessa técnica e, sempre que possível, procuro incentivar os desenvolvedores a experimentarem o sabor dos testes.

Lembre-se disso: “O teste é o primeiro cliente do seu código e o ditado diz que o cliente sempre tem a razão.”

SAIBA MAIS: Conheça os erros que você não deve cometer ao escrever testes unitários!

  • Marcio Guedes

    Ótimo post!

    Gostaria de acrescentar minha opinião: aqui no Brasil se tem uma idéia meio errada sobre testes unitários e eu acredito que o JUnit tem uma parcela de culpa nisso por falta de entendimento do conceito de testes unitários. Testes unitários começam definindo qual é a unidade a ser testada e eles podem ser aplicados a qualquer segmento de trabalho, não somente a informática.

    Por exemplo, se você colocou um prego na parede, então sua unidade é verificar se o prego está bem fixado. O teste unitário pra isso é pendurar o quadro e ele não cair no chão. Se quiser um teste unitário mais efetivo basta pendurar um quadro mais pesado.

    Ou seja, pouca gente sabe, mas está executando testes unitários a cada minuto!

    O JUnit é um framework que auxilia na definição de testes unitários automatizados e o foco dele é na menor unidade de código java que é o método. Porém nem sempre o teste unitário pode ser automátizado. As vezes o seu teste unitário pode ser criar um main (que depois será excluído do código), outras vezes o seu teste unitário pode ser verificar comportamento de tela, que é um teste unitário visual. Esses testes unitários não automatizados frequentemente são esquecidos ou negligenciados pelos desenvolvedores.

    Por exemplo, se você fez uma classe java que vai imprimir um “Hello World” no console, você acabou de definir a unidade a ser testada: verificar se a mensagem será impressa no console! Pra testar essa unidade não tem sentido usar o JUnit, pois o teste unitário pra isso é simplesmente compilar, executar e verificar se a mensagem apareceu no console.

    Acho que a grande mensagem aqui é que os desenvolvedores devem se conscientizar que testes unitários fazem parte do cronograma de desenvolvimento e, automatizados ou manuais, devem ser executados sempre!

    Isso é o que eu acho, mas eu posso estar enganado!

  • JONATHAS BATISTA BARROSO

    Faz 5 meses que trabalho numa empresa onde o teste de software é levado a sério. Antes tinha dificuldades em entender cenários e criá-los em código.
    Hoje posso ver toda a praticidade em verificar meus códigos em questão de segundos por testes automatizados e me sinto muito perdido quando um teste não é implementado.
    Quando uma pessoa pede que eu o auxilie, pergunto logo: cadê o teste ? pois é muito mais fácil identificar e sanar o problema desse jeito , do que debugar todo a execução.
    Claro que durante o período de adaptação é ruim, mas depois de você alcançar os resultados, vocês vão ver como os testes ajudam e são boas pedidas.

  • Bom artigo! Parabéns !

  • Vinicius

    Parabéns pelo post! Continue assim

  • Julio Cesar

    Parabéns pelo Artigo!.

    Estou achando muito interessante esse tema abordado, tive a oportunidade de conhecer um pouco no livro ” Use a Cabeça Desenvolvimento de Software”.

  • Pingback: Continuous Integration | Felipe Firmo()

  • Hugo Luiz

    O post ajudou para minha apresentação de Eng de Software. Muito bom elaborado.

  • Pingback: Testes unitários com PHPUnit | GABRIELJMJ()

  • Igor

    Ola ,

    Sou iniciante na area de teste e estagiario em uma empresa de tecnologia . Fui encarregado de aprender a ferramenta selenium para fazer teste automatizados no sistema da empresa , porém meu chefe deseja que eu use essa ferramenta para saber se o usuario ao fazer um cadastro de um produto ele foi devidamente incluido no banco ou seja saber se o usuario ao inserir os dados na pagina web conseguio fazer a opcao desejada inserção , alteraçao , exclusão ou atualizar (CRUD).

    Att . Igor

  • Bruno Andrade

    Ótimo artigo, estou terminando o curso de Analise em Desenvolvimento de Sistemas, e o meu (TCC),trata exatamente esse assunto de Teste, e esse artigo ta sendo a base do meu artigo.

    • Obrigado, Bruno. Muito bom saber. Se possível for, gostaria de ler o seu TCC, pois o assunto muito me interessa.