TDD e BDD na Prática: Guia de Implementação para Desenvolvedores e Times Ágeis
Entenda como TDD e BDD funcionam na prática. Aprenda a aplicar o ciclo Red-Green-Refactor, a estruturar cenários em Gherkin e a integrar as duas metodologias para elevar a qualidade do seu software.
Você já passou pela frustração de entregar uma funcionalidade tecnicamente impecável, apenas para ouvir do Product Owner (PO) que “não era bem isso que o negócio precisava”? Ou, pior, já sentiu aquele frio na barriga ao alterar uma linha de código em um sistema legado porque tudo parecia prestes a quebrar?
Esses problemas não são causados por falta de esforço, mas sim por falhas de comunicação e pela ausência de uma rede de segurança técnica. No desenvolvimento de software, o Test-Driven Development (TDD) e o Behavior-Driven Development (BDD) surgem não como burocracias acadêmicas, mas como guias práticos de design, alinhamento e arquitetura que economizam tempo no dia a dia do desenvolvedor.
Neste guia prático, vamos desmistificar essas duas metodologias, entender como elas se complementam e ver como aplicá-las em cenários reais.
O que é TDD (Test-Driven Development) e o ciclo Red-Green-Refactor
O TDD (Test-Driven Development) é uma metodologia de desenvolvimento de software onde você escreve o teste unitário antes de escrever o código de produção. Essa inversão de fluxo força o desenvolvedor a pensar no design da API, na usabilidade do código e nos casos de borda antes mesmo de começar a codificar.
A base do TDD é o ciclo Red-Green-Refactor (Vermelho-Verde-Refatorar), que funciona em três etapas estritas:
- Red (Vermelho): Escreva um teste unitário para uma funcionalidade ou comportamento que ainda não existe. Execute o teste e certifique-se de que ele falha (geralmente porque a classe ou o método sequer foi implementado).
- Green (Verde): Escreva a menor quantidade de código de produção possível para fazer o teste passar. Não se preocupe com elegância, performance ou boas práticas aqui; o único objetivo é sair do vermelho.
- Refactor (Refatorar): Agora que o teste está passando e você tem uma rede de segurança, limpe o código. Remova duplicidades, melhore nomes de variáveis, extraia métodos e aplique padrões de design. Execute os testes novamente para garantir que a refatoração não quebrou o comportamento existente.
Essa abordagem melhora drasticamente a qualidade de software, pois garante que 100% do código escrito seja testável e que o sistema possua uma cobertura de testes robusta desde o primeiro dia.
O que é BDD (Behavior-Driven Development) e o papel da linguagem ubíqua
Enquanto o TDD foca em garantir que o código seja escrito corretamente (perspectiva técnica), o BDD (Behavior-Driven Development) foca em garantir que o software certo seja construído (perspectiva de negócio). Criado por Dan North como uma evolução do TDD, o BDD direciona o desenvolvimento com base no comportamento esperado do usuário.
O coração do BDD é a linguagem ubíqua: uma linguagem comum, sem jargões técnicos, compartilhada por desenvolvedores, QAs, designers e stakeholders de negócios. Ao usar termos que todos entendem, evitam-se falhas de comunicação clássicas onde o desenvolvedor interpreta um requisito de forma totalmente diferente do que o cliente final deseja.
No BDD, os requisitos de negócio são traduzidos em cenários de comportamento executáveis, servindo tanto como documentação viva quanto como testes de aceitação automatizados.
Sintaxe Gherkin na prática: Estruturando cenários com Dado-Quando-Então
Para escrever esses cenários de comportamento, utilizamos uma linguagem de especificação chamada Gherkin. Ela segue uma estrutura lógica muito simples baseada em três pilares fundamentais:
- Dado (Given): Define o contexto inicial ou o estado do sistema.
- Quando (When): Descreve a ação executada pelo usuário ou pelo sistema.
- Então (Then): Define o resultado esperado ou a consequência da ação.
Vamos a um cenário real de um carrinho de compras onde precisamos validar a aplicação de um cupom de desconto:
Funcionalidade: Aplicar cupom de desconto no carrinho
Como um cliente da loja virtual
Quero aplicar um cupom de desconto válido no meu carrinho
Para obter um abatimento no valor total da minha compra
Cenário: Aplicação de cupom de desconto ativo com sucesso
Dado que eu tenho um carrinho com um item de valor R$ 100.00
E o cupom "VALE10" está ativo e concede 10% de desconto
Quando eu aplico o cupom "VALE10" ao meu carrinho
Então o valor do desconto deve ser R$ 10.00
E o valor total a pagar deve ser R$ 90.00
Qualquer pessoa no time, técnica ou não, consegue ler o cenário acima e validar se a regra de negócio está correta.
Exemplo prático de código: Do teste à refatoração
Vamos ver como implementar a lógica do cupom de desconto utilizando o ciclo Red-Green-Refactor do TDD em JavaScript (com Jest).
Passo 1: Red (Escrever o teste que falha)
Primeiro, criamos o nosso arquivo de teste carrinho.test.js definindo o comportamento esperado da nossa classe Carrinho:
const Carrinho = require('./carrinho');
test('deve aplicar cupom de desconto de 10% corretamente', () => {
const carrinho = new Carrinho();
carrinho.adicionarItem({ nome: 'Camiseta', preco: 100.00 });
carrinho.aplicarCupom({ codigo: 'VALE10', porcentagem: 10, ativo: true });
expect(carrinho.obterDesconto()).toBe(10.00);
expect(carrinho.obterTotal()).toBe(90.00);
});
Se executarmos o Jest agora, o teste vai falhar (Red) porque a classe Carrinho sequer existe.
Passo 2: Green (Escrever o código mínimo para passar)
Agora, criamos o arquivo carrinho.js com a implementação mais simples possível para fazer o teste passar:
class Carrinho {
constructor() {
this.itens = [];
this.cupom = null;
}
adicionarItem(item) {
this.itens.push(item);
}
aplicarCupom(cupom) {
this.cupom = cupom;
}
obterDesconto() {
if (!this.cupom) return 0;
return 10.00; // Hardcoded apenas para passar o teste inicial
}
obterTotal() {
return 90.00; // Hardcoded apenas para passar o teste inicial
}
}
module.exports = Carrinho;
Executamos o teste e… Green! O teste passou. Mas o código está ruim e cheio de valores fixos.
Passo 3: Refactor (Melhorar o código)
Com a segurança do teste passando, vamos refatorar a lógica para torná-la dinâmica e robusta:
class Carrinho {
constructor() {
this.itens = [];
this.cupom = null;
}
adicionarItem(item) {
this.itens.push(item);
}
aplicarCupom(cupom) {
if (cupom && cupom.ativo) {
this.cupom = cupom;
}
}
obterSubtotal() {
return this.itens.reduce((total, item) => total + item.preco, 0);
}
obterDesconto() {
if (!this.cupom) return 0;
const subtotal = this.obterSubtotal();
return (subtotal * this.cupom.porcentagem) / 100;
}
obterTotal() {
const subtotal = this.obterSubtotal();
const desconto = this.obterDesconto();
return subtotal - desconto;
}
}
module.exports = Carrinho;
Rodamos os testes novamente. Eles continuam passando, mas agora o código está limpo, dinâmico e pronto para receber novos cenários (como cupons expirados ou carrinhos vazios).
Como TDD e BDD se complementam no fluxo de desenvolvimento
TDD e BDD não são concorrentes. Na verdade, eles funcionam melhor quando integrados no que chamamos de Double Loop (Ciclo Duplo) de desenvolvimento:
- Loop Externo (BDD): O time define um cenário de comportamento em Gherkin (teste de aceitação). Esse teste falha porque a funcionalidade não existe.
- Loop Interno (TDD): Para fazer esse cenário de BDD passar, o desenvolvedor entra no ciclo Red-Green-Refactor, escrevendo múltiplos testes unitários para as classes, funções e APIs internas que darão suporte à funcionalidade.
- Fechamento: Quando todos os testes unitários (TDD) passam, o comportamento macro (BDD) também passa, completando o ciclo de entrega.
Essa abordagem se encaixa perfeitamente na pirâmide de testes, onde o BDD atua no topo/meio (testes de aceitação e integração) e o TDD atua na base (testes unitários rápidos e isolados). Para entender melhor onde cada um se encaixa, vale a pena conhecer os diferentes tipos de teste de software.
Colaboração na Prática: O Fluxo de Trabalho do QA e do Dev
Antes de qualquer linha de código ser escrita, a colaboração acontece da seguinte forma:
[Product Owner] ──(Define a Regra de Negócio)──>
[Reunião dos Três Amigos]
[QA / Analista] ──(Propõe Casos de Borda)─────> (PO, Dev e QA refinam e
escrevem o arquivo Gherkin)
[Developer] ────(Avalia Viabilidade Técnica)──>
│
▼
[Cenário Gherkin Pronto]
│
┌───────────────────────┴───────────────────────┐
▼ ▼
[Dev: Codifica usando TDD] [QA: Prepara automação/cenários]
Esse alinhamento prévio garante que, quando o desenvolvedor começa a codificar, não há dúvidas sobre o comportamento esperado do sistema.
Ferramentas populares para implementar TDD e BDD
Para colocar essas metodologias em prática, o ecossistema de tecnologia oferece excelentes ferramentas de testes automatizados:
Ferramentas de TDD (Testes Unitários e de Integração)
- Jest / Vitest: Extremamente populares no ecossistema JavaScript/TypeScript.
- JUnit: O padrão da indústria para projetos Java.
- PyTest: Uma das ferramentas mais elegantes e utilizadas em Python.
- RSpec: Clássico do ecossistema Ruby, muito focado em legibilidade.
Ferramentas de BDD (Automação de Cenários Gherkin)
- Cucumber: A ferramenta de BDD mais conhecida do mercado, com suporte a Java, JavaScript, Ruby e outras linguagens.
- Behave: Excelente framework de BDD para projetos Python.
- SpecFlow: A principal alternativa para desenvolvedores do ecossistema .NET.
Desafios comuns na adoção por times pequenos (e como superá-los)
Adotar TDD e BDD em times enxutos ou startups pode parecer desafiador no início. Aqui estão os principais obstáculos e como superá-los sem perder produtividade:
- A sensação de “escrever código em dobro”: No início, escrever testes e cenários parece desacelerar as entregas. Como superar: Foque o TDD apenas nas regras de negócio críticas (como cálculo de preços, checkout e integrações de pagamento). Não tente testar 100% do sistema logo de início.
- Cenários de BDD que viram manuais técnicos: Evite escrever passos como
Quando eu clico no botão id="submit". O BDD deve descrever intenções de negócio (Quando eu finalizo a compra), não detalhes de implementação de interface. - Falta de tempo para manter os testes: Testes frágeis que quebram a qualquer mudança visual desmotivam o time. Como superar: Mantenha os testes de unidade focados em lógica pura e use o BDD para fluxos de ponta a ponta cruciais.
Comece pequeno. Escolha uma funcionalidade isolada na próxima sprint, reúna o time para escrever um único cenário Gherkin e implemente-o usando o ciclo Red-Green-Refactor. Com o tempo, a confiança no código compensará amplamente o investimento inicial.
FAQ (Perguntas Frequentes)
TDD e BDD são metodologias concorrentes?
Não. Elas são complementares. O BDD foca no comportamento do sistema sob a perspectiva do usuário e do negócio (o que construir), enquanto o TDD foca na qualidade técnica e na estrutura do código sob a perspectiva do desenvolvedor (como construir corretamente).
Quem deve escrever os cenários de BDD?
A escrita deve ser um esforço colaborativo conhecido como “Três Amigos” (Product Owner/Product Manager, Desenvolvedor e QA). O PO traz o requisito de negócio, o QA traz os cenários de exceção e o Dev avalia a viabilidade técnica, garantindo que todos falem a mesma língua antes do código começar.
Escrever testes primeiro não vai atrasar as entregas do meu time?
No curtíssimo prazo, pode parecer que sim devido à curva de aprendizado. No entanto, ao evitar retrabalho, bugs em produção e sessões intermináveis de depuração manual, a combinação de TDD e BDD reduz o custo total de manutenção e acelera o ritmo de entrega sustentável do time.
Referências
- FOWLER, Martin. Test Driven Development. Disponível em: https://martinfowler.com/bliki/TestDrivenDevelopment.html.
- CUCUMBER. What is BDD?. Disponível em: https://cucumber.io/docs/bdd/.
Sobre Marcos Costa
Desenvolvedor backend com foco em arquitetura de software, automação e produtos digitais.
Ver mais artigos

