Em 2003, Dan North começou a escrever sobre uma frustração específica de ensinar TDD: alunos travavam não na mecânica do ciclo, mas em decisões como "o que devo testar primeiro?", "que nome dar?", "como sei se cobri o bastante?". TDD nada disso prescrevia. North percebeu que o vocabulário do TDD — o uso da palavra test em todo lugar — desviava o foco do problema real, que era especificação de comportamento. Daí veio o Behavior-Driven Development (BDD): uma evolução do TDD que substituía vocabulário e introduzia a fórmula Given/When/Then. Não é uma tecnologia diferente; é uma forma diferente de pensar e nomear.
A ideia central de BDD: testes deveriam ler como especificações executáveis do comportamento esperado. "Given a customer with $100 in their account, when they withdraw $30, then their balance should be $70." Essa estrutura, quando aplicada com integridade, faz os testes serem documentação viva — alguém que nunca viu o código consegue ler os testes e entender o que o sistema faz. E quando vira fluência, muda como você pensa o problema antes mesmo de escrever código.
O que motivou BDD
North observou três problemas recorrentes em times tentando praticar TDD:
- Confusão sobre o que testar: alunos não sabiam por onde começar. "Test the class? Test methods? Test the API?". TDD clássico não dava resposta.
-
Nomes ruins:
testFunction1(),testHappyPath(),testValidation(). Nomes descreviam mecânica, não comportamento. Em três meses, ninguém sabia o que cada teste verificava sem ler o corpo. - Alienação de stakeholders: testes eram escritos por devs, em código, longe de quem definia requisitos. Conversa entre negócio e dev acontecia em documentos paralelos que ficavam desatualizados.
BDD endereçou os três:
- O que testar: comportamentos do sistema do ponto de vista de stakeholder, não unidades de implementação.
-
Como nomear:
should_decrease_balance_when_withdrawingem vez detestWithdraw1. O nome é uma frase que diz o que o sistema deve fazer. - Quem participa: stakeholders escrevem cenários em linguagem natural; devs traduzem para automação. Os cenários ficam próximos do produto.
Given/When/Then — a fórmula
A estrutura de cada cenário tem três partes:
-
Given: o estado inicial. "Dado que existe um cliente
com saldo $100." Configura contexto. Pode haver vários
Given(em Gherkin, conectados comAnd). - When: a ação que estamos testando. "Quando ele saca $30." Idealmente uma única ação central, sem ambiguidade.
- Then: o resultado esperado. "Então o saldo dele é $70." Pode ter múltiplos asserts.
A fórmula força clareza. Se você não consegue articular o cenário em G/W/T, há boa chance de não saber direito o que está testando. É ferramenta de pensamento.
O mesmo no nível de código (sem framework)
Você não precisa de Cucumber ou SpecFlow para aplicar G/W/T. Pode ser apenas estrutura mental para o teste, com comentários explícitos:
[Fact]
public void Withdrawing_Decreases_Balance() {
// Given
var account = new Account(initialBalance: 100);
// When
account.Withdraw(30);
// Then
account.Balance.Should().Be(70);
}
Isto é BDD-style sem ferramentas extras. Variantes comuns no mundo
C# usam // Arrange / Act / Assert; em Python frequentemente
omitem comentários e fazem por estrutura visual; em Go é "table-driven"
onde cada linha é um cenário. Todas são variações do mesmo princípio.
Gherkin e os frameworks BDD
Quando o time inclui stakeholders no processo, surge a necessidade de uma sintaxe que negócio consiga ler e escrever. Gherkin é essa sintaxe: cenários em texto plano, em estrutura padrão, depois ligados a código por step definitions.
Feature: Account withdrawal
As an account holder
I want to withdraw money
So that I can use it
Scenario: Successful withdrawal
Given an account with balance 100
When I withdraw 30
Then the balance should be 70
Scenario: Insufficient funds
Given an account with balance 100
When I withdraw 200
Then the withdrawal should fail
And the balance should remain 100
Step definitions ligam cada linha em texto a código:
// C# com SpecFlow
[Given(@"an account with balance (\d+)")]
public void GivenAccountWithBalance(int balance) {
_account = new Account(balance);
}
[When(@"I withdraw (\d+)")]
public void WhenIWithdraw(int amount) {
_result = _account.Withdraw(amount);
}
[Then(@"the balance should be (\d+)")]
public void ThenBalanceShouldBe(int expected) {
_account.Balance.Should().Be(expected);
}
Frameworks principais por linguagem: Cucumber (criado em Ruby, hoje multi-linguagem), SpecFlow (.NET, agora Reqnroll após mudança de mantenedor), behave (Python), godog (Go), pytest-bdd (Python alternativa, integrada ao pytest). Todos consomem Gherkin com pequenas variações.
Onde BDD com Gherkin brilha
O ferramental compensa em situações específicas:
- Domínios de negócio complexos com stakeholders engajados: seguros, finanças, regulamentos. Onde regras são externas e ambíguas, escrever Given/When/Then com o stakeholder força clarificação que documento normal não consegue.
- Times com BAs (Business Analysts) ativos no processo: BAs podem escrever os cenários em texto plano, devs implementam steps. Co-design real.
- Sistemas com regras combinatórias amplas: tabelas de decisão complicadas (impostos, descontos) ficam claras em Gherkin com Scenario Outline e Examples table.
- Documentação viva como requisito do negócio: regulamentações que exigem documentação rastreável de comportamento (auditoria, compliance) — Gherkin gera relatórios legíveis.
Onde BDD vira teatro
Em outros contextos, Gherkin é overhead pesado que produz testes piores que os equivalentes em código direto. Sintomas de BDD mal-aplicado:
Stakeholder não escreve nem lê os cenários
Se devs estão escrevendo Gherkin para devs lerem, você adicionou uma camada de tradução sem ganho. Os mesmos testes em xUnit ou pytest seriam mais diretos. BDD-em-Gherkin custa o engajamento real do negócio; sem ele, é cerimônia.
Steps quase iguais multiplicando-se
"Given user with email john@x.com", "Given user with email mary@y.com", "Given user that has no email"... Cada variação vira um step novo. Gherkin força frases naturais; código permite parametrização limpa. Em projetos com muitas variações, testes ficam mais curtos e claros em código.
Indireção desnecessária
Para entender o que um cenário Gherkin testa, você precisa ler o cenário, encontrar os steps no código, e ler eles. Para entender um teste em xUnit, você lê o teste. Para casos onde o ganho de legibilidade-pelo-stakeholder não existe, a indireção é puro custo.
Lentidão de execução
Frameworks BDD adicionam parsing de Gherkin, matching de regex, reflection. Em suítes grandes, isso vira centenas de milissegundos por teste — somando minutos no total. Para suítes em Tier 1 do CI, é problema.
A regra prática: se o time tem stakeholders ativos no processo e o domínio tem regras de negócio complexas, BDD com Gherkin paga. Caso contrário, G/W/T como estrutura mental dentro de testes em código colhe a maior parte do benefício sem o overhead.
Times adotam Gherkin "porque é melhor prática" sem stakeholders comprados. Resultado típico: dois meses depois, devs estão escrevendo e mantendo Gherkin sozinhos, e ninguém do negócio nunca abriu os arquivos. Custou a adoção, custou velocidade, sem ganho. Se o motivo de usar Gherkin é "deveríamos ter testes legíveis para o time", comece sem Gherkin — código bem nomeado já cobre 90%.
O lado mais valioso de BDD — a conversa
Frequentemente esquecido: BDD não é primariamente sobre ferramentas. É sobre conversas estruturadas entre negócio e tech antes de codar. A prática que North chama de "Three Amigos" — produto, dev e QA discutindo cada cenário antes da implementação — é onde acontece a maior parte do valor.
Nessa conversa:
- Produto descreve o comportamento desejado.
- Dev questiona casos de borda, ambiguidades, integrações.
- QA pensa em cenários adversariais (o que pode dar errado?).
- Os três chegam num conjunto de cenários concretos antes do código existir.
Esses cenários — escritos em Gherkin ou em qualquer outro formato — viram a especificação compartilhada. Quando o dev volta para implementar, sabe exatamente o que precisa fazer. Quando o QA testa, sabe o que verificar. Quando produto valida, sabe o que esperar.
O ponto é: você pode ter essa conversa sem usar Cucumber. A conversa é o valor. O ferramental ajuda quando ele alimenta a conversa e dificulta quando ele a substitui por cerimônia.
Acceptance criteria — o nível imediatamente acima
Antes de cenários BDD, há acceptance criteria em stories/tickets — listas de condições que precisam ser atendidas para a story ser considerada pronta. AC bem-escrita já tem estrutura G/W/T implícita:
Story: User can withdraw from account
Acceptance Criteria:
- Given an account with sufficient balance,
when user withdraws an amount,
then balance is decreased by that amount
and a transaction record is created.
- Given an account with insufficient balance,
when user attempts withdrawal,
then the operation fails
and balance is unchanged
and the user sees error message "Insufficient funds".
- Given an account that is locked (suspect activity),
when user attempts any withdrawal,
then the operation fails
and the user sees "Account locked, contact support".
Essa AC é executável conceitualmente — vira teste 1:1. Times maduros
escrevem AC nesse formato como prática padrão (em Jira, Linear, etc).
Quando alguém quer formalizar, vira Gherkin trivial. Quando o time
prefere testes em código, viram [Fact] ou
def test_* com a mesma estrutura.
BDD em testes de aceitação E2E
Onde BDD com Gherkin se mantém forte mesmo em times sem BA é em testes de aceitação end-to-end. Esses testes são tipicamente poucos (dezenas, não centenas), focados em fluxos críticos, e revisados por produto. O custo de Gherkin se dilui, e a documentação viva tem público real.
Feature: Order checkout
Scenario: User completes purchase with valid card
Given a user logged in with cart containing a $50 item
When they enter valid card details and submit checkout
Then the order is created with status "confirmed"
And a confirmation email is sent to the user
And the cart is emptied
Scenario: Card declined
Given a user logged in with cart containing a $50 item
When they enter card details for a card that will be declined
Then the order is not created
And the user sees "Payment declined - try another card"
And the cart still contains the $50 item
Cenários assim viram suíte E2E pequena, executada em pre-deploy, e legível por produto/QA/dev. Um pouco de Gherkin onde ele ajuda; sem Gherkin no resto.
Como praticar
-
Reescreva nomes de testes existentes em estilo BDD.
Pegue 10 testes seus. Renomeie de
test_validate_orderparaorder_should_fail_when_total_exceeds_limit. Note como o nome agora é a especificação. - Aplique G/W/T como estrutura, não Gherkin. Pegue uma feature do seu projeto. Antes de implementar, escreva 5 cenários em texto plano com G/W/T. Implemente. Note como o esforço de articular antecipa decisões de design.
- Experimente Gherkin num caso pequeno. Configure SpecFlow/behave/godog num projeto pequeno. Escreva 2-3 cenários. Sinta o overhead. Decida com evidência se vale para seu contexto.
Referências para aprofundar
- livro BDD in Action (2nd ed.) — John Ferguson Smart (2023).
- livro Specification by Example — Gojko Adzic (2011).
- livro The Cucumber Book (2nd ed.) — Wynne & Hellesoy (2017).
- artigo Introducing BDD — Dan North (2006).
- artigo What's in a Story? — Dan North (2007).
- artigo The 3 Amigos — George Dinwiddie.
- artigo Given-When-Then is Two Things — Liz Keogh.
- docs Cucumber Documentation.
- docs Reqnroll Documentation.
- docs pytest-bdd Documentation.
- vídeo BDD - Common Misconceptions — Liz Keogh.
- vídeo The Three Amigos — George Dinwiddie (Agile2009).