BDD: The Core Concept

Behavior-Driven Development (BDD) has been around for years, but its association with Gherkin, the language used by tools like Cucumber, has led to a common misconception: that BDD necessitates writing feature files. Paul Brooks, a veteran of acceptance testing since 2009, argues this is a false equivalence. He states unequivocally that he has “never written a feature file by choice” and has always practiced BDD without them. The core of BDD, Brooks emphasizes, is a methodology for specifying software behavior, not a rigid adherence to a particular file format or tool.

Gherkin’s primary purpose is to enable non-developers, such as testers or business analysts, to write executable specifications in plain text without needing to touch code. If a project’s workflow involves these roles directly authoring and running `.feature` files from the repository, then Cucumber and Gherkin serve their intended function effectively. However, Brooks points out that this ideal scenario is rarely encountered in practice. On nearly every team he’s observed, acceptance criteria, often in a Given-When-Then format, are initially drafted within issue tracking systems like Jira, not in dedicated feature files.

This leads to a disconnect. The criteria are written, but they often remain as static text within a ticket, failing to be integrated into the automated testing suite. The effort to translate these criteria into executable code, even if the criteria themselves follow a BDD-like structure, becomes a separate, often manual, step. This process can be error-prone and time-consuming, undermining the agility that BDD aims to provide.

Bridging the Gap: Integrating BDD into Developer Workflows

Brooks advocates for a more integrated approach where BDD principles are applied directly within the developer's workflow. Instead of relying on external `.feature` files that require a separate translation step, behavior can be specified using code constructs that are directly executable. Tools and frameworks exist that allow developers to write Given-When-Then scenarios using programming languages like Java, C#, or JavaScript. This approach eliminates the need for a separate Gherkin parsing layer and keeps the executable specifications close to the code they describe.

Consider a scenario where a new feature requires handling user authentication. In a Gherkin-centric approach, this might start with a `.feature` file:

Feature: User Authentication

  Scenario: Successful login with valid credentials
    Given the user is on the login page
    When the user enters valid username "testuser" and password "password123"
    And the user clicks the login button
    Then the user should be redirected to the dashboard

This file would then need corresponding step definitions written in a programming language to make it executable. The problem arises when the step definitions themselves become complex, or when the initial Gherkin writing happens in Jira, requiring manual transcription and potential misinterpretation.

Alternatively, Brooks’s preferred method involves writing these scenarios directly in code. Using a BDD framework integrated into a testing library (like JUnit for Java, NUnit for C#, or Jest for JavaScript), the same scenario could be expressed as:

@Test
public void successfulLoginWithValidCredentials() {
    given("the user is on the login page", () -> {
        // Code to navigate to login page
    }).when("the user enters valid username \"testuser\" and password \"password123\"", () -> {
        // Code to input credentials
    }).and("the user clicks the login button", () -> {
        // Code to click login
    }).then("the user should be redirected to the dashboard", () -> {
        // Assertion for redirection
    });
}

This code-based approach offers several advantages. Firstly, it keeps the executable specification and its implementation in the same place, reducing the cognitive load and the potential for drift between specification and code. Secondly, it leverages the full power of the programming language, allowing for more complex logic, data manipulation, and integration with existing codebases. It also means that the people writing the specifications are typically developers, who are already comfortable with code and can directly translate business requirements into executable tests.

The Real Cost of Gherkin Translation

The implicit assumption in many Gherkin-driven projects is that the translation from plain text to executable code is trivial. Brooks argues this is often not the case. When acceptance criteria are written in Jira tickets, they are essentially specifications in a natural language, albeit structured. The process of turning these into Gherkin, and then into step definitions, involves multiple human interpretations. Each interpretation point is an opportunity for error or loss of nuance.

What nobody has addressed yet is the accumulated cost of this translation process across large projects and organizations. The time spent writing Gherkin, maintaining it separately from the application code, and debugging step definition mismatches can become substantial. This overhead can slow down development cycles and obscure the actual behavior the system is supposed to exhibit. Brooks’s experience suggests that bypassing Gherkin altogether and writing executable specifications directly in code streamlines this process, making tests more robust and development more efficient.

Think of it less like writing instructions for a robot that only understands a specific command language (Gherkin) and more like writing a very precise set of instructions for a skilled engineer (the developer) who already speaks the primary language (the programming language). The latter is often more direct and less prone to misinterpretation.

When Gherkin Might Still Make Sense

Despite his preference, Brooks acknowledges that Gherkin and tools like Cucumber have a valid place. If a project genuinely benefits from business stakeholders or non-technical team members directly authoring and executing tests, Gherkin provides that capability. This is particularly true in scenarios where the team structure and domain expertise align with the strengths of Gherkin. For instance, in highly regulated industries where business analysts must have direct, auditable input into test specifications, Gherkin can be invaluable.

However, for many software development teams, particularly those where developers are the primary authors of acceptance criteria and tests, the overhead of Gherkin can be a hindrance. The practice of writing criteria in tickets and then translating them into Gherkin represents a common deviation from the ideal Gherkin workflow. In these cases, adopting BDD principles directly in code offers a more pragmatic and efficient path to achieving the goals of Behavior-Driven Development.

The key takeaway is that BDD is a mindset and a practice focused on collaborative understanding and specification of behavior. Gherkin is merely one tool, and often not the most efficient one, for implementing that practice within a developer-centric workflow.