O ciclo Red‑Green‑Refactor do TDD transforma design em pequenos passos verificáveis: escreva um teste que falha, implemente o mínimo para passar e então refatore com segurança. Este guia prático mostra o fluxo, exemplos em React/TypeScript e como TDD traz valor técnico e de negócio.
November 29, 2025 (4mo ago) — last updated January 2, 2026 (3mo ago)
Red‑Green‑Refactor TDD: Guia Prático
Domine o ciclo Red‑Green‑Refactor do TDD com exemplos práticos em React/TypeScript e técnicas para código mais limpo e previsível.
← Back to blog
Red‑Green‑Refactor TDD: Guia Prático
Resumo: Domine o ciclo Red‑Green‑Refactor de Test‑Driven Development (TDD) com um fluxo prático, exemplos em React/TypeScript e benefícios de negócio que ajudam a manter código limpo e confiável.
Introdução
O ciclo Red‑Green‑Refactor do Test‑Driven Development (TDD) é um processo simples e disciplinado que guia o design do software. Comece escrevendo um teste que falha (Red), escreva o código mínimo para fazê‑lo passar (Green) e então limpe a implementação (Refactor). Repetir esse ciclo converte incerteza em passos pequenos e verificáveis, reduzindo riscos e melhorando a qualidade do software desde cedo.1
O ritmo do desenvolvimento guiado por testes

Muitos desenvolvedores pensam que TDD é só sobre testes, quando na prática é uma técnica de design. Escrever o teste primeiro obriga você a pensar em como o código será usado antes de implementá‑lo. Essa inversão reduz suposições e promove progresso em pequenos passos. O ciclo Red‑Green‑Refactor passa a ser o compasso do desenvolvimento confiável.
As três fases em poucas palavras
- Fase Red (teste falhando): escreva um teste automatizado que descreva o menor comportamento útil. O teste deve falhar porque a implementação não existe.
- Fase Green (fazer passar): implemente a menor quantidade de código necessária para satisfazer o teste. Priorize simplicidade para evitar over‑engineering.
- Fase Refactor (melhorar o código): com os testes passando como rede de segurança, limpe nomes, remova duplicação e melhore a estrutura mantendo o comportamento.
Não pule a refatoração; ela evita acúmulo de dívida técnica e facilita mudanças futuras.
Resumo do ciclo
| Fase | Propósito | Objetivo do desenvolvedor |
|---|---|---|
| Red | Definir requisito e validar o teste | Escrever um teste pequeno que falhe |
| Green | Satisfazer o requisito | Adicionar o código mínimo para passar o teste |
| Refactor | Melhorar qualidade interna | Limpar duplicação e esclarecer intenção |
Adotar essa cadência ajuda equipes a avançar de forma previsível. Em várias pesquisas sobre adoção ágil, equipes relatam benefícios de colaboração e qualidade ao incorporar práticas como TDD1.
Percorrendo o ciclo TDD na prática

Vamos construir um exemplo de UI: um componente LikeButton usando TypeScript, React e Jest. O exemplo mostra como TDD orienta o design mantendo comportamento previsível.
Fase Red: definir o primeiro requisito
Requisito inicial: o componente renderiza sem travar e exibe “Like”. Escrevemos o teste antes do componente.
// LikeButton.test.tsx
import React from "react";
import { render, screen } from "@testing-library/react";
import LikeButton from "./LikeButton";
describe("LikeButton", () => {
it("renders a button with the initial text \"Like\"", () => {
render(<LikeButton />);
const likeButton = screen.getByRole("button", { name: /like/i });
expect(likeButton).toBeInTheDocument();
});
});
Executar o teste falha porque o componente ainda não existe. Esta é a fase Red — exatamente o que queremos.
Fase Green: o suficiente para passar
Crie o componente mínimo para satisfazer o teste.
// LikeButton.tsx
import React from "react";
const LikeButton = () => {
return <button>Like</button>;
};
export default LikeButton;
Execute os testes novamente e eles passam. Missão cumprida para este ciclo.
Fase Refactor: polir a implementação
Agora limpe o código. Adicione tipos e estabeleça um padrão para expansão futura.
// LikeButton.tsx (refatorado)
import React, { FC } from "react";
type LikeButtonProps = {};
const LikeButton: FC<LikeButtonProps> = () => {
return <button>Like</button>;
};
export default LikeButton;
Os testes continuam passando. A rede de segurança permite melhorar o código com confiança.
Iteração: clicar no botão
Novo requisito: clicar no botão altera seu texto para “Liked” e o desabilita para evitar múltiplos cliques. Comece com um teste que falha.
// LikeButton.test.tsx
it("changes text to \"Liked\" and becomes disabled when clicked", () => {
render(<LikeButton />);
const likeButton = screen.getByRole("button", { name: /like/i });
fireEvent.click(likeButton);
expect(likeButton).toHaveTextContent("Liked");
expect(likeButton).toBeDisabled();
});
Implemente o comportamento mínimo para passar no teste.
// LikeButton.tsx
import React, { FC, useState } from "react";
type LikeButtonProps = {};
const LikeButton: FC<LikeButtonProps> = () => {
const [liked, setLiked] = useState(false);
const handleClick = () => setLiked(true);
return (
<button onClick={handleClick} disabled={liked}>
{liked ? "Liked" : "Like"}
</button>
);
};
export default LikeButton;
Execute a suíte e tudo fica verde. Repita: um requisito pequeno por vez, protegido por testes.
O caso de negócio para qualidade de código

Os benefícios técnicos do TDD se traduzem em valor de negócio. Menos defeitos em produção significam custos de suporte menores, menor churn de clientes e melhor reputação. Quando defeitos são detectados cedo, custam menos para corrigir; equipes podem gastar mais tempo criando funcionalidades que geram valor.2
Reduzindo defeitos pós‑release e custos de manutenção
Escrever testes antes do código força a adicionar apenas o que é necessário para satisfazer requisitos testáveis. Isso cria uma rede de segurança robusta e reduz regressões. Estudos empíricos mostram melhorias mensuráveis de qualidade e redução do esforço de depuração para equipes que aplicam TDD consistentemente2.
Acelerando o onboarding e melhorando a previsibilidade
Uma suíte de testes funciona como documentação executável. Novos desenvolvedores podem rodar os testes para entender o comportamento esperado do sistema em vez de depender de documentação desatualizada. Isso encurta o tempo de ramp‑up e reduz a carga sobre engenheiros seniores. Práticas de TDD também tornam estimativas e acompanhamento de progresso mais previsíveis, o que melhora o planejamento com stakeholders3.
Armadilhas comuns do TDD e como evitá‑las
TDD é simples de explicar, mas sutil de dominar. Os anti‑padrões abaixo costumam minar os benefícios.
Testes de integração embalados como unitários
Problema: o teste acaba exercitando muitas dependências — componentes, serviços, clientes de API e bancos de dados. O teste fica lento e frágil.
Solução: teste uma única unidade em isolamento. Use mocks, stubs e fakes para dependências externas. Se precisar de cobertura de integração, escreva testes de integração separados que rodem em uma suíte distinta.
Um teste unitário verdadeiro não deve tocar a rede, o sistema de arquivos ou um banco de dados real; sua velocidade e confiabilidade permitem refatorar com segurança4.
Testar implementação em vez de comportamento
Problema: os testes afirmam detalhes internos em vez do comportamento público. Testes quebram quando internals mudam, mesmo que o comportamento esteja correto.
Solução: foque na API pública e nos efeitos observáveis. Pergunte: dado este input, qual é o output esperado? Testes que verificam comportamento resistem a refatorações e continuam sendo documentação valiosa.
Pular a refatoração
Problema: desenvolvedores vão direto para a próxima funcionalidade depois de fazer os testes passarem, deixando implementações bagunçadas.
Solução: trate a refatoração como etapa obrigatória. Com os testes passando, pequenas limpezas são seguras e evitam acúmulo de problemas.
Integrando TDD à sua equipe e ao código legado

Adotar TDD é mudança cultural e técnica. Incentive aprendizado prático, inclua testes na Definição de Pronto e celebre vitórias orientadas por testes.
Promovendo TDD na equipe
Formas práticas de ganhar adesão:
- Pair programming para guiar colegas pelo ciclo Red‑Green‑Refactor.
- Mob programming para problemas complexos, rodiziando quem dirige para espalhar conhecimento.
- Sessões de aprendizado que demonstrem TDD na base de código.
Comece pequeno e deixe que ganhos cedo se tornem prova do valor.
Domando código legado com testes de caracterização
Quando o código não tem testes e é arriscado de alterar, escreva testes de caracterização para documentar o comportamento atual. Esses testes permitem refatorar ou adicionar funcionalidades com confiança.
Automatizando qualidade com pipelines CI/CD
Execute a suíte de testes em cada commit no CI. Isso fornece feedback imediato, impõe gates de qualidade e torna a passagem dos testes obrigatória antes do merge. A automação mantém o loop de feedback rápido e confiável3.
Perguntas frequentes rápidas
O TDD substitui outros tipos de testes?
Não. TDD foca em testes unitários como ferramenta de design, mas você ainda precisa de testes de integração e end‑to‑end para validar interações entre componentes e fluxos completos de usuário.
Como usar TDD com bancos de dados ou APIs externas?
Isole dependências externas usando mocks, stubs ou fakes. Teste a lógica em isolamento e deixe testes de integração reais para uma suíte separada.
Vale a pena testar componentes simples de UI?
Sim, quando o teste verifica comportamento observável. Testes que afirmam o que o usuário vê e faz são úteis e resistentes a refatoração.
Perguntas e respostas — resumo em 3 pontos
P: Quanto tempo até minha equipe ver valor com TDD?
A: Ganhos aparecem rapidamente na redução de regressões; pequenas vitórias geralmente surgem em poucos sprints.
P: Qual é o menor primeiro passo para adotar TDD?
A: Exija um teste que falhe antes de implementar uma funcionalidade nova ou um bug não crítico, e imponha a refatoração.
P: Como convencer stakeholders a investir em testes?
A: Mostre economias a longo prazo — menos incidentes em produção e custos de manutenção menores — e exemplos concretos de problemas evitados.
IA escreve código.Você faz durar.
Na era da aceleração da IA, código limpo não é apenas uma boa prática — é a diferença entre sistemas que escalam e bases de código que entram em colapso sob seu próprio peso.