TDD vale a pena quando os casos de uso já estão definidos antes de escrever código; sem essa clareza, ele vira retrabalho. A regra é simples: crie o teste antes da aplicação apenas quando você sabe exatamente o que entregar, e pule a prática em exploração e provas de conceito descartáveis.
O que é TDD e por que ele depende de casos de uso claros
TDD (Test-Driven Development) é a prática de escrever o teste antes do código de produção, seguindo o ciclo vermelho-verde-refatorar: o teste falha, o código mínimo faz passar, e só então o código é refatorado. Kent Beck, engenheiro de software americano, formalizou a prática no livro Test-Driven Development: By Example, publicado pela Addison-Wesley em 2002.
O pré-requisito que quase ninguém menciona é ter os casos de uso definidos antes de começar. Wesley Willians, instrutor da Full Cycle, descreve essa fricção em uma palestra publicada no canal da Full Cycle em 26 de novembro de 2024: quem desenvolve sem checklist tende a moldar o requisito durante o código, e o teste escrito antes de o requisito existir vira retrabalho.
A alternativa prática é montar um checklist simples com campos, validações e comportamentos esperados. Com esse checklist em mãos, o teste passa a documentar o requisito, e o desenvolvedor sabe por onde começar.
Os três tipos de teste e o custo real de cada um
Testes de unidade verificam a menor parte do software, sem dependências externas; testes de integração exercitam um módulo que conversa com banco de dados, API ou outras camadas; testes end-to-end percorrem o sistema inteiro. Os três formam a pirâmide de testes, e o custo cresce de baixo para cima.
- Testes de unidade rodam em milissegundos, não tocam banco nem rede e cobrem regras de negócio.
- Testes de integração validam um fluxo funcional e normalmente exigem banco de dados, filas ou APIs externas.
- Testes end-to-end simulam o usuário final, rodam contra um ambiente próximo de produção e são os mais lentos da esteira de CI/CD.
Willians descreve o teste end-to-end como o mais caro de todos, porque precisa chamar banco de dados e chegar o mais próximo possível de produção. Nenhum dos três resolve o problema sozinho: unidade cobre regras, integração valida fluxos, end-to-end confirma que o conjunto funciona.
Quando testar um agregado inteiro ainda conta como teste de unidade
Testar um agregado inteiro ainda conta como teste de unidade quando as classes são fortemente acopladas e não dependem de ambiente externo. Essa é a leitura de Willians, que descreve tratar Pedido e Item como uma unidade única quando o domínio os mantém inseparáveis.
Em Domain-Driven Design, um agregado é um conjunto de entidades e objetos de valor que compartilham uma fronteira de consistência. Uma entidade tem identidade própria, representada por um ID, enquanto um objeto de valor é imutável e não possui identidade. Quando Pedido e Item formam um agregado sem acesso a banco ou API, criar dois testes separados adiciona trabalho sem ganho de cobertura.
A ressalva aparece quando o objeto de valor concentra validações complexas. Nesse caso, um teste dedicado apenas a ele faz sentido. O critério é a coesão e a ausência de dependência externa, não a contagem de classes envolvidas.
Mocks e stubs: como lidar com dependências externas
Mocks e stubs substituem dependências externas para que testes de integração não precisem acessar banco de dados, gateway de pagamento ou API de terceiros a cada execução. Um mock simula comportamento condicional: dada uma entrada, devolve uma saída específica, inclusive um erro.
Um stub devolve dados fixos. Willians exemplifica com um banco falso que responde a uma consulta com um array de objetos em memória. Ambos dependem de uma interface comum, o que permite trocar a implementação real pela simulação sem alterar o código de produção.
O ponto de atenção é o custo inicial. Criar um banco falso ou uma classe que simula comportamentos dá trabalho, mas esse investimento é feito uma vez. A alternativa, acessar o serviço real a cada execução, consome tempo e recursos computacionais em toda rodada da esteira.
O ciclo vermelho-verde-refatorar sem pular etapas
O ciclo vermelho-verde-refatorar tem três etapas obrigatórias: escrever um teste que falha, implementar o código mínimo para passar e refatorar com a suíte verde. Pular a etapa vermelha remove a única prova de que o teste realmente verifica algo.
- Escreva o teste com o resultado esperado para uma entrada específica.
- Rode a suíte e confirme que o teste falha pelo motivo certo.
- Implemente apenas o necessário para o teste passar.
- Refatore o código mantendo os testes verdes.
- Repita para o próximo caso de uso do checklist.
Willians alerta para os dois extremos. Implementar mais do que o teste exige cobre casos ainda não especificados; testar getters e setters gerados pela linguagem consome tempo sem verificar comportamento. Se o teste é óbvio, implemente de uma vez; se envolve encadeamento de lógica acumulada, avance em passos pequenos.
Quando não usar TDD: exploração, PoC e regras voláteis
TDD deve ficar de fora em trabalho exploratório, provas de conceito (PoC) descartáveis e código cujas regras de negócio ainda estão sendo definidas. Nesses cenários, o requisito muda antes de o teste compensar o esforço de escrevê-lo.
Willians relata um período em que trabalhava com computação visual e não dominava os filtros nem os métodos disponíveis. Ele escrevia scripts pequenos para descobrir o comportamento antes de qualquer teste. Em uma PoC que provavelmente será jogada fora, o mesmo raciocínio se aplica.
O terceiro caso é o código volátil. Se a empresa redefine as regras com frequência, cada mudança obriga a reescrever os testes junto com o código. A recomendação de Willians é esperar a poeira baixar: quando o requisito estabiliza, você aplica os testes, melhora a cobertura e segue com TDD a partir dali.
Como escolher entre TDD, testes depois e nenhum teste
A escolha entre TDD, testes escritos depois e nenhum teste depende de três variáveis: clareza do requisito, volatilidade das regras e custo de manter a suíte atualizada. Nenhuma das três opções é universal, e tratar TDD como regra absoluta ignora o contexto do projeto.
| Cenário | Abordagem indicada | Motivo |
|---|---|---|
| Requisito claro e estável | TDD desde o primeiro teste | O teste vira documentação viva do requisito |
| Requisito claro, mas entregue sob pressão | Testes depois, com cobertura crescente | Entrega primeiro, rede de segurança em seguida |
| Exploração ou aprendizado de tecnologia | Nenhum teste no início | O comportamento ainda é desconhecido |
| PoC descartável | Nenhum teste, salvo o conceito | O código não vai para produção |
| Domínio já estável, com cobertura baixa | Testes de caracterização antes de refatorar | Preserva o comportamento atual |
Willians resume a posição dele de forma direta: se a coisa está funcionando e o requisito ficou claro, aplique os testes, melhore a cobertura e trabalhe com TDD a partir dali. Fora disso, ele não trabalharia com TDD.
A verificação na linguagem escolhida vale para qualquer decisão de teste. No ecossistema TypeScript, ferramentas como Jest e Vitest rodam testes de unidade em milissegundos, o que torna o ciclo de feedback viável mesmo em bases grandes.
Perguntas frequentes sobre TDD
- TDD serve para qualquer projeto? Não. TDD funciona melhor quando os casos de uso já estão definidos e as regras mudam pouco. Em exploração, provas de conceito descartáveis e domínios voláteis, a prática tende a gerar retrabalho maior do que o benefício.
- Qual é a diferença entre teste de unidade e teste de integração? O teste de unidade verifica a menor parte do software e não usa dependências externas. O teste de integração valida um fluxo ou módulo que conversa com banco de dados, API ou outras camadas.
- Testar um agregado inteiro conta como teste de unidade? Conta quando as classes são fortemente acopladas e não dependem de ambiente externo. Se o objeto de valor concentra validações complexas, um teste dedicado apenas a ele faz sentido.
- O que é um mock e como ele difere de um stub? Um mock simula comportamento condicional, devolvendo saídas diferentes conforme a entrada. Um stub devolve dados fixos, como uma lista em memória que finge ser um banco de dados.
- Por que o teste end-to-end é o mais caro da esteira? Porque ele percorre o sistema inteiro, chama banco de dados e exige um ambiente próximo de produção. Ele é o mais lento do pipeline e o que mais consome recursos.
- TDD realmente deixa o desenvolvimento mais lento? A sensação é essa, porque o ciclo exige pensar antes de codar. O tempo gasto descobrindo o requisito durante o código é o mesmo que seria gasto escrevendo os casos de uso antes, com menos retrabalho no fim.
- Como começar com TDD na prática? Monte um checklist com campos, validações e comportamentos esperados antes de escrever qualquer linha. Comece pelo caso mais simples, siga o ciclo vermelho-verde-refatorar e avance em passos pequenos.
- Quando os testes devem ser escritos depois do código? Quando o requisito está claro, a entrega é urgente e a base ainda não tem cobertura. Nesse caso, o teste posterior funciona como rede de segurança, e o TDD entra quando o domínio estabilizar.
- Testar getters e setters faz sentido? Não, salvo quando existe validação no acesso. A linguagem de programação já garante o comportamento básico de leitura e escrita, e o teste apenas repete o que o compilador ou interpretador assegura.
Fork this article
Start a new branch from the same video, shaped your way. You keep the credit; the original keeps the attribution.
A fork in another language is filed as a translation of this article, so the two pages point at each other. You can unlink it later from the editor.
0/240
You are creating
- Format
- For
- Language
- Source
- Your angle
You will be asked to sign in before it is generated.
Buy credits