Um teste unitário é um pedaço de código que executa outro pedaço de código e verifica que o resultado é o que você esperava. Nada mais. Você pega uma função, dá a ela uma entrada conhecida, e confere que ela devolve a saída correta. Se coincide, o teste passa. Se não, ele falha e avisa você antes que um usuário o faça.
Essa é a resposta curta. A resposta útil é entender por que esse teste, o menor de todos, é a base sobre a qual tudo o mais se apoia. Chama se "unitário" porque examina uma unidade: a menor peça do seu programa que faz sentido testar em separado, quase sempre uma função ou um método.
01 · a baseO que é um teste unitário e o que o torna "unidade"
Pense na pirâmide de testes, um modelo que organiza os tipos de teste por quantidade e velocidade. Na base vão muitos testes unitários: rápidos, baratos, isolados. No meio, menos testes de integração, que verificam se várias peças se encaixam. E no topo, poucos testes de ponta a ponta, que percorrem o sistema completo como uma pessoa faria. A forma de pirâmide não é decorativa: ela diz quantos testes de cada tipo convém ter. Martin Fowler popularizou essa figura a partir do trabalho de Mike Cohn [1].
O que torna um teste unitário uma "unidade" é o isolamento. Ele testa uma única coisa, sem depender do banco de dados, sem chamar a rede, sem tocar no relógio do sistema nem ler um arquivo. Se o seu teste precisa de tudo isso para rodar, ele já não é unitário: é de integração, e pertence a outro nível da pirâmide.
Se você não consegue testar uma função sem ligar meia aplicação, o problema não é o teste: é o desenho da função.
Essa restrição tem um efeito colateral valioso. Escrever o teste obriga você a raciocinar sobre o que a função recebe, o que devolve e do que depende. Uma função difícil de testar costuma ser uma função que faz demais, ou que esconde dependências. O teste unitário é, sem querer, um detector de mau desenho.
02 · o padrãoArrange, act, assert: a anatomia de todo teste
Quase todo teste unitário bem escrito tem três partes, sempre na mesma ordem. Em inglês é conhecido como AAA: arrange, act, assert. Em português: preparar, agir, afirmar.
Preparar (arrange). Você monta o cenário: cria os dados de entrada e qualquer objeto de que a função precise. Agir (act). Você chama a função que quer testar, uma única vez, com esses dados. Afirmar (assert). Você compara o que ela devolveu com o que você esperava. Se não coincide, o teste falha.
Vejamos o exemplo mínimo. Suponha uma função que soma dois números. O teste se lê quase como uma frase.
Esse assert é o coração. Uma afirmação que diz, sem ambiguidade, o que tem de ser verdade. Quando a função quebrar no futuro, porque alguém a mexeu sem querer, essa linha será a que dispara o alarme. Um bom teste unitário tem idealmente uma afirmação clara: testa uma coisa e dá um motivo concreto quando falha.
O erro de principiante é escrever um teste que passa sempre, mesmo com a função quebrada. Antes de confiar em um teste, quebre o de propósito: mude a função para que dê um resultado incorreto e confirme que o teste falha. Se ele continua passando em verde com a função avariada, o seu teste está olhando para o lado errado.
03 · o valorPor que são rápidos, baratos e a primeira linha de defesa
Um teste unitário roda em milissegundos porque não toca em nada externo. Você pode ter milhares e executá los todos em segundos, cada vez que salva uma mudança. Esse ciclo curtíssimo é o que os torna baratos: o custo de escrever um é baixo, e o custo de executá lo é quase zero. Kent Beck construiu o desenvolvimento guiado por testes justamente sobre essa ideia, escrever o teste antes do código para que o desenho nasça já verificado [2].
O valor não está em um teste, mas em tê los todos rodando juntos. Quando você muda uma função e sem querer quebra outra a dez arquivos de distância, o teste dessa outra função falha na hora. Você fica sabendo em segundos, não em produção. Isso é uma rede de segurança, e é o que permite modificar código com confiança em vez de com medo.
Sem testes unitários, cada mudança é uma aposta. Com eles, é uma hipótese que se verifica no instante.
A ISTQB, o organismo internacional de certificação de testes, situa o teste unitário como o primeiro nível de teste, o mais próximo do código e o que executam quem o escreve [3]. Não é por acaso que seja o primeiro: quanto antes se pega um defeito, mais barato sai corrigi lo. Uma falha detectada por um teste unitário custa minutos; a mesma falha descoberta por um cliente custa reputação.
Há um limite honesto que convém dizer. Os testes unitários verificam que cada peça funciona em separado, mas não que o sistema completo funciona junto. Você pode ter cem funções perfeitas que, montadas, fazem algo incorreto. Para isso existem os outros níveis da pirâmide. O teste unitário não é toda a qualidade: é a sua base. E uma base sólida é a condição para que tudo o que vai em cima se sustente.
Comece pelo pequeno. Pegue a função mais simples que você tiver, uma que receba algo e devolva algo, e escreva a ela um teste com os três atos: prepara uma entrada, chama a, afirma a saída. Quebre a para confirmar que o teste avisa. Esse primeiro teste em verde, que roda num piscar de olhos, é a menor unidade de confiança que você pode construir sobre o seu código. Tudo o mais se apoia ali.
Fuentes
- Fowler, M. (2012). Test Pyramid. martinfowler.com/bliki/TestPyramid.html. (Populariza la figura a partir de Cohn, M., Succeeding with Agile, Addison-Wesley, 2009.)
- Beck, K. (2002). Test-Driven Development: By Example. Addison-Wesley Professional. ISBN 978-0321146533.
- ISTQB (International Software Testing Qualifications Board). Certified Tester Foundation Level Syllabus, sección sobre niveles de prueba (component/unit testing). istqb.org.
- ISO/IEC/IEEE 29119-4. Software and systems engineering. Software testing. Part 4: Test techniques. International Organization for Standardization.