TDD vs BDD: principais diferenças, um recurso criado de ambas as maneiras e quando usar cada um

  1. Início
  2. Blog
  3. Automação de testes

Automação de Teste

TDD vs BDD: Principais diferenças, um recurso criado de ambas as maneiras e quando usar cada um

TDD ajuda os desenvolvedores a projetar o código correto. O BDD ajuda toda a equipe a concordar sobre o que o software deve fazer. Veja o mesmo recurso de desconto criado com cada abordagem e saiba como os dois funcionam juntos.

9 min de leitura
TDD vs BDD: principais diferenças, um recurso construído em ambos os sentidos e quando usar cada um

Principais pontos

  • TDD significa escrever primeiro um teste com falha, depois apenas código suficiente para passar e depois arrumar.
  • BDD descreve o comportamento em linguagem compartilhada, geralmente como Dado, Quando, Então.
  • O TDD orienta principalmente o design do código. O BDD constrói principalmente um entendimento compartilhado.
  • Eles não são rivais e muitas equipes usam BDD para recursos e TDD para detalhes abaixo.

O debate TDD vs BDD muitas vezes é enquadrado como a escolha de um lado. Na realidade, eles respondem a perguntas diferentes. O desenvolvimento orientado a testes pergunta se o código faz o que o desenvolvedor pretendia, enquanto o desenvolvimento orientado a comportamento pergunta se a equipe concorda com o que o software deveria fazer.

Para tornar a diferença concreta, construímos um pequeno recurso de desconto em cada abordagem. Executamos o código no Python 3.13, usando a biblioteca padrão, e os resultados abaixo vêm dessa execução. A parte do BDD usa um pequeno corredor próprio para mostrar o mecanismo, então um projeto real usaria uma ferramenta como Cucumber ou se comportaria em vez disso.

A resposta curta

Desenvolvimento orientado a testes (TDD) é uma prática de codificação. Você escreve um pequeno teste com falha, faz com que ele passe com o código mais simples e depois melhora o design. Os testes são escritos em código e principalmente por desenvolvedores. Kent Beck popularizou a prática.

Desenvolvimento orientado ao comportamento (BDD) é uma prática de colaboração que surgiu do TDD, e Dan North introduziu o termo. Desenvolvedores, testadores e empresários concordam em exemplos de como o sistema deve se comportar, escritos em linguagem simples, e esses exemplos podem então ser automatizados.

TDD vs BDD lado a lado

Aspecto TDD BDD
Foco principal Código correto e bem projetado Compreensão compartilhada do comportamento
Escrito em Linguagem de programação Linguagem simples, como pepino
Escrito por Desenvolvedores Negócios, controle de qualidade e desenvolvedores juntos
Legível por Desenvolvedores Todos da equipe
Nível típico Unidade Recurso ou aceitação
Ponto de partida Um teste com falha Uma conversa com exemplos
Ciclo Vermelho, verde, refatorar Descubra, formule, automatize
Ferramentas comuns pytest, JUnit, brincadeira Pepino, comporte-se, SpecFlow, Reqnroll

O recurso

Aqui está a regra de negócios que implementaremos: Pedidos de 1.000 ou mais recebem 10% de desconto. Os membros ganham 5% de desconto extra nesses pedidos.

Isso pode parecer claro, mas já é ambíguo. Um membro que gasta 500 ganha 5% de desconto? A frase diz “essas ordens”, então presumimos que não. Tenha isso em mente, porque esse é exatamente o tipo de questão que o BDD foi projetado para trazer à tona antecipadamente.

Construindo com TDD

O TDD segue um pequeno loop conhecido como red, green, refactor:

Passo O que você faz
Vermelho Escreva um teste para comportamento que ainda não existe e observe-o falhar
Verde Escreva o código mais simples que faça passar
Refatorar Melhore o código, mantendo os testes verdes

Estes são nossos testes, com um para cada regra:

class DiscountTests(unittest.TestCase):
    def test_no_discount_below_1000(self):
        self.assertEqual(discount(999, False), 0)

    def test_ten_percent_from_1000(self):
        self.assertEqual(discount(1000, False), 100)

    def test_member_gets_extra_five_percent(self):
        self.assertEqual(discount(1000, True), 150)

Em seguida, escrevemos o código em três estágios, executando os testes após cada um. Uma sessão estrita de TDD adiciona um teste por vez e mostra a mesma progressão:

Etapa 1: a coisa mais simples

def discount(total, is_member):
    return 0

# 3 tests, 2 failed
#   test_member_gets_extra_five_percent
#   test_ten_percent_from_1000

Retornar zero parece bobo, mas prova que os testes podem realmente falhar, e o único teste em que ele passa é a regra “sem desconto”.

Etapa 2: adicionar o limite

def discount(total, is_member):
    if total >= 1000:
        return total * 0.1
    return 0

# 3 tests, 1 failed
#   test_member_gets_extra_five_percent

Etapa 3: adicionar membros e limpar

def discount(total, is_member):
    rate = 0.1 if total >= 1000 else 0
    if is_member:
        rate += 0.05
    return round(total * rate, 2)

# 3 tests, 0 failed

Todos os três passam. Mas leia esta versão com atenção. Dá ao membro que gasta 500 um desconto de 5%, o que não é o que presumimos que a regra significava. Nenhum teste cobriu esse caso, então nada o sinalizou. O TDD protege apenas o comportamento que você pensou em testar. Essa é uma limitação real e a razão pela qual as conversas sobre exemplos são tão importantes.

Por que o ciclo é importante

  • O vermelho prova que o teste funciona. Um teste que nunca falhou pode não estar testando nada.
  • Verde mantém você focado. Você escreve apenas o código necessário, o que ajuda a evitar excesso de engenharia.
  • Refactor mantém alta qualidade. Os testes dão a você a confiança necessária para alterar o design com segurança.
  • Isso influencia o design. O código que é difícil de testar geralmente está muito acoplado e você percebe imediatamente.

Nosso tutorial pytest mostra uma maneira mais conveniente de escrever testes como esses, e a análise de valor limite explica por que escolhemos 999 e 1000 como valores.

Construindo com BDD

O BDD começa com uma conversa, não com ferramentas. Muitas vezes é uma reunião curta conhecida como os três amigos, com um responsável pelo produto, um desenvolvedor e um testador discutindo exemplos concretos:

Tester:  What about a member spending 500?
Product: Good question. No discount. It has to reach 1000 first.
Dev:     And exactly 1000 counts?
Product: Yes, 1000 or more.

Essa troca teria detectado o problema em nosso código TDD. Eles então registram seus exemplos em um formato estruturado chamado Gherkin:

Feature: Order discounts

  Scenario: Member reaches the discount threshold
    Given a member with a cart worth 1000
    When they view the amount to pay
    Then the amount to pay is 850

  Scenario: Member below the threshold gets no discount
    Given a member with a cart worth 500
    When they view the amount to pay
    Then the amount to pay is 500
Palavra-chave Objetivo
Dado A situação inicial
Quando A ação ou evento
Então O resultado esperado

Qualquer pessoa da equipe pode ler isso e dizer “sim, está certo” ou “não, está errado”. Mesmo que você nunca o automatize, o cenário já realizou um trabalho útil.

Conectando etapas ao código

As ferramentas conectam cada linha de linguagem simples a uma função, chamada definição de etapa. Para mostrar a ideia, nós mesmos escrevemos uma pequena versão e executamos o primeiro cenário:

@step("a member with a cart worth 1000")
def given(ctx):
    ctx["cart"] = Cart(is_member=True)
    ctx["cart"].add(1000)

@step("they view the amount to pay")
def when(ctx):
    ctx["amount"] = ctx["cart"].payable()

@step("the amount to pay is 850")
def then(ctx):
    assert ctx["amount"] == 850

# ok Given a member with a cart worth 1000
# ok When they view the amount to pay
# ok Then the amount to pay is 850

Ferramentas reais adicionam parâmetros, para que uma única etapa possa corresponder a “1000” e “500”, além de relatórios. O resultado é documentação viva: uma descrição simples do sistema que falha ruidosamente se deixar de ser verdade.

Dica: O valor do BDD está na conversa. As equipes que têm uma pessoa escrevendo Gherkin sozinha após a conclusão do código recebem o custo da ferramenta, com quase nenhum benefício.

Escrevendo bons cenários

Fraco Melhor
Quando clico no botão com id "btn-77" Quando aplico o cupom
Então o resultado está correto Então o valor a pagar é 850
Vinte etapas em um cenário Um comportamento por cenário
Descreve a tela Descreve a intenção do negócio

Os cenários que descrevem cliques e IDs de campo são interrompidos a cada reformulação e os leitores empresariais param de lê-los. Mantenha os detalhes da interface do usuário no código da etapa. Esses cenários são primos próximos dos exemplos em caso de teste versus cenário de teste, e também são frequentemente usados ​​em testes de aceitação do usuário.

Usando os dois juntos

Os dois se encaixam naturalmente em um fluxo de trabalho “de fora para dentro”:

  1. A equipe concorda com o comportamento e escreve um cenário BDD. Ele falha, porque nada existe ainda.
  2. Um desenvolvedor usa TDD para construir as pequenas unidades necessárias.
  3. Os testes de unidade são aprovados e, eventualmente, o cenário também.
  4. Tudo é executado em seu pipeline de CI/CD.

Mantenha cada um no nível certo. Você deve ter alguns cenários para comportamentos importantes e muitos testes de unidade rápidos para detalhes. Nosso guia para testes de unidade versus integração cobre o equilíbrio.

Ferramentas

Idioma TDD (teste unitário) BDD
Pitão pytest, unittest comporte-se, pytest-bdd
JavaScript Brincadeira, Vitest Cucumber.js
Java JUnit, TestNG Pepino-JVM
.NET xUnit, NUnit Reqnroll, SpecFlow

Quando usar cada

TDD se adapta bem quando:

  • Você está escrevendo lógica complexa, como preços, análise ou permissões.
  • Você está corrigindo um bug, pois pode reproduzi-lo primeiro com um teste com falha.
  • Você está trabalhando em um código que mudará frequentemente.

O BDD se adapta bem quando:

  • As regras de negócios são complexas e os mal-entendidos custam caro.
  • Pessoas não técnicas realmente lerão e discutirão os cenários.
  • Várias equipes precisam de uma definição compartilhada de como algo se comporta.

Pode ser um exagero quando: você está escrevendo um protótipo descartável ou explorando uma ideia técnica pouco clara. Muitos desenvolvedores aumentam primeiro e depois reescrevem com testes. As ferramentas BDD também são um investimento ruim se ninguém fora da engenharia analisar os cenários. Nesse caso, testes simples com bons nomes são mais simples.

Erros comuns

  • Tratar o BDD como uma ferramenta de teste. Cucumber sem colaboração é apenas uma maneira detalhada de escrever testes.
  • Testando implementação em TDD. Testes vinculados a detalhes privados são interrompidos durante cada refatoração.
  • Ignorando a etapa de refatoração. Você passa nos testes além de um código confuso.
  • Escrever testes após o código e chamá-lo de TDD. Isso ainda é útil, mas você perde o feedback do design.
  • Escrever muitos cenários de UI. Eles se tornam lentos e instáveis. Consulte testes instáveis.
  • Perseguir números de cobertura. Uma boa cobertura ainda ignora comportamentos que ninguém considerou, como mostra nosso exemplo.

Considerações finais

O TDD ajuda você a construir a coisa certa e o BDD ajuda a construir a coisa certa. Nosso exemplo de desconto mostrou os dois lados: o código passou em todos os testes, mas uma simples conversa revelou uma suposição errada. Experimente em seu próximo pequeno recurso. Escreva três exemplos concretos com um colega antes de qualquer código e, em seguida, construa um teste com falha de cada vez.

Perguntas frequentes

Qual é a principal diferença entre TDD e BDD?

O TDD se concentra em desenvolvedores que escrevem testes de código com falha antes da implementação. O BDD se concentra em uma equipe inteira concordando com o comportamento usando exemplos em linguagem simples.

Você pode usar TDD e BDD juntos?

Sim, e muitas equipes o fazem. Os cenários BDD definem recursos externos e o TDD orienta as unidades menores de código abaixo deles.

O BDD é melhor que o TDD?

Nenhum dos dois é melhor no geral, pois resolvem problemas diferentes. O BDD ajuda no entendimento compartilhado e o TDD ajuda no design do código e no feedback rápido.

O que é pepino?

Gherkin é um formato estruturado de linguagem simples para escrever cenários, usando palavras-chave como Dado, Quando e Então. Ferramentas como o Cucumber podem executá-lo.

O TDD retarda o desenvolvimento?

Pode parecer mais lento no início, especialmente durante o aprendizado. As equipes geralmente recuperam tempo com menos bugs e mudanças mais seguras, embora o efeito varie de acordo com o projeto.

Continue aprendendo

Trabalhe com Stack Quora

Precisa de ajuda com testes, WordPress ou SEO?

Ajudamos as equipes a fornecer software melhor e a expandir seus sites. Conte-nos no que você está trabalhando e sugeriremos um próximo passo claro.

Continue lendo

Mais guias sobre testes, IA e desenvolvimento moderno

Gostou deste artigo? Escolha um tópico e vá direto para nossos tutoriais mais recentes.

Inteligência artificial

Explicação das alucinações de IA: por que os modelos de linguagem inventam as coisas e como reduzi-las

Explicação das alucinações de IA: o que são, por que os modelos de linguagem as produzem, tipos comuns, como reduzi-las com aterramento e instruções…

Leia o artigo →
DevOps

Variáveis de ambiente explicadas: arquivos .env, segredos e configuração bem feitos

Variáveis de ambiente explicadas com exemplos de Python que executamos: leitura e configuração, por que cada valor é uma string, arquivos .env, hera…

Leia o artigo →
Desenvolvimento de software

Explicação da limitação de taxa de API: algoritmos, cabeçalhos e tratamento de erros 429

Limitação de taxa de API explicada com uma simulação que executamos: token bucket, janela fixa, janela deslizante, 429 respostas, cabeçalhos de limi…

Leia o artigo →
Automação de testes

Zombando em Python: unittest.mock, patch e os erros que fazem os testes mentirem

Zombaria em Python com unittest.mock, testado: Mock e return_value, side_effect, patch onde um nome é usado, especificação e autospec, zombaria de d…

Leia o artigo →
Teste de software

Cobertura de código explicada: cobertura de linha versus filial e o que 100% realmente significa

Cobertura de código explicada com uma execução cobertura.py real: cobertura de linha, ramal e condição, leitura de um relatório, por que 100% de cob…

Leia o artigo →
Bancos de dados e SQL

DELETE vs TRUNCATE vs DROP em SQL: diferenças, reversão e quando usar cada um

DELETE vs TRUNCATE vs DROP explicado com SQL testado: o que cada um remove, rollback, aciona, redefinição de identidade, velocidade e como PostgreSQ…

Leia o artigo →
Bancos de dados e SQL

Exibições SQL explicadas: crie, use e evite as armadilhas, com exemplos

Visualizações SQL explicadas com consultas que executamos: criação de uma visualização, resultados ao vivo, ocultação de colunas, desempenho, visual…

Leia o artigo →
Desenvolvimento de software

Interface TypeScript vs tipo: diferenças reais, testadas com o compilador

Interface TypeScript versus tipo, verificada com o compilador real: o que ambos podem fazer, o que apenas um pode fazer, mesclagem de declarações, e…

Leia o artigo →
Desenvolvimento de software

Python *args e **kwargs explicados: funções flexíveis com exemplos claros

Python *args e **kwargs explicados com o código que executamos: empacotamento e descompactação, ordem dos parâmetros, argumentos somente de palavras…

Leia o artigo →
Desenvolvimento de software

Funções Python Lambda: sintaxe, usos reais e quando não usá-los

Funções lambda do Python explicadas com o código que executamos: sintaxe, classificação com chave, mapa e filtro, fechamentos, armadilha de ligação …

Leia o artigo →
Inteligência artificial

O que são agentes de IA? Como eles funcionam, com um loop de agente simples que você pode executar

O que são agentes de IA? Um guia simples para o loop do agente, ferramentas, memória e proteções, com um pequeno loop Python que executamos, riscos …

Leia o artigo →
DevOps

Monitoramento versus observabilidade: a verdadeira diferença, com logs, métricas e rastreamentos

Explicação do monitoramento versus observabilidade: falhas conhecidas e desconhecidas, logs, métricas e rastreamentos, um passo a passo do incidente…

Leia o artigo →
DevOps

Docker Volumes vs Bind Mounts: persista os dados do contêiner da maneira certa

Volumes Docker versus montagens de ligação explicadas: por que os dados do contêiner desaparecem, volumes nomeados, montagens de ligação, tmpfs, exe…

Leia o artigo →
Automação de testes

TDD vs BDD: principais diferenças, um recurso criado de ambas as maneiras e quando usar cada um

TDD vs BDD explicado com um recurso de desconto construído em ambos os sentidos: refatoração vermelho-verde com o código que executamos, cenários da…

Leia o artigo →
Desenvolvimento de software

Git merge vs rebase: diferenças, exemplos e quando usar cada um

Git merge vs rebase explicado com saída real do log, uma tabela comparativa, a regra de ouro do rebase e um fluxo de trabalho seguro para equipes.

Leia o artigo →
Desenvolvimento de software

List comprehension em Python: sintaxe, exemplos e quando evitar

List comprehension em Python explicada: sintaxe, filtros, if-else, laços aninhados, tempos medidos e quando um laço for comum é a melhor escolha.

Leia o artigo →
Bancos de dados e SQL

SQL JOIN explicado com exemplos (Inner, Left, Right, Full)

Entenda os joins SQL passo a passo com tabelas e consultas de exemplo: INNER, LEFT, RIGHT, FULL, CROSS e SELF, além de erros comuns e dicas de desem…

Leia o artigo →