Skip to main content
Pinpoint
Testing

Cucumber Testing: BDD Done Right

Pinpoint Team8 min read

Cucumber testing bridges a gap that most test frameworks ignore: the gap between what engineers verify and what the business expects. By expressing test scenarios in plain English using Gherkin syntax, cucumber makes acceptance criteria executable. Product managers can read the tests, QA engineers can write them without deep coding knowledge, and developers can implement them knowing exactly what "done" means. For startup teams where miscommunication between roles is a leading cause of rework, that clarity has measurable value. This guide covers how to adopt cucumber testing effectively without falling into the traps that make BDD feel like overhead.

What cucumber testing actually solves

The core promise of behavior-driven development is that everyone involved in a feature should agree on its behavior before anyone starts building it. In practice, this agreement often exists informally: a product manager writes a ticket, a developer interprets it, and discrepancies surface during code review or, worse, after deployment.

Cucumber formalizes that agreement using a structured language called Gherkin. A scenario looks like this: Given a user with a valid subscription, When they access the premium dashboard, Then they see their usage metrics for the current billing period. That scenario is both a specification and a test. When the step definitions are implemented, running the scenario verifies that the described behavior actually works.

The benefit is not the Gherkin syntax itself. It is the conversation that writing Gherkin forces. When a product manager, a developer, and a tester sit down to write scenarios together, ambiguities surface immediately. "What happens if the subscription expired yesterday?" becomes a scenario rather than an assumption. Teams that practice this "three amigos" session consistently report 20 to 30 percent fewer defects traced to misunderstood requirements.

Structuring feature files for maintainability

A feature file in cucumber maps to a capability or workflow in your application. The most common structural mistake is creating feature files that are too granular or too broad. A file for "User clicks the save button" is too narrow. A file for "User management" is too broad. The right level is usually a user workflow: "User registration," "Subscription upgrade," or "Invoice generation."

Within each feature file, scenarios should be independent. Every scenario should set up its own preconditions in the Given steps rather than relying on a previous scenario's side effects. This independence means scenarios can run in any order, which matters for parallelization, and a failure in one scenario does not cascade into false failures in subsequent ones.

Several structural practices keep feature files useful over time:

  • Keep scenarios under 10 steps. If a scenario requires 15 Given/When/Then lines, it is probably testing multiple behaviors. Split it into focused scenarios that each verify one outcome.
  • Use Background for shared preconditions. When every scenario in a feature starts with the same Given steps, move them into a Background block. This reduces duplication without sacrificing readability.
  • Use Scenario Outline for data-driven tests. When the same workflow needs to be verified with different inputs (valid email, invalid email, empty email), a Scenario Outline with an Examples table is cleaner than three separate scenarios with identical steps.
  • Write steps in domain language, not UI language. "When the user registers with email admin@example.com" is better than "When the user clicks the email field and types admin@example.com and clicks the register button." Domain-level steps survive UI redesigns. UI-level steps break every time you change a form layout.

Implementing step definitions that last

Step definitions are the code that connects Gherkin steps to actual test logic. They are where most cucumber implementations succeed or fail, because poorly written step definitions create the maintenance burden that makes teams abandon BDD entirely.

The key principle is reusability. A step like "Given a user with a valid subscription" should be implemented once and used across every feature file that needs a subscribed user. This means step definitions should accept parameters for variable data and avoid hardcoding specifics that change between scenarios.

Cucumber supports step definitions in most major languages. Java teams typically use cucumber-java with JUnit 5. JavaScript teams use cucumber-js. Python teams use behave, which follows the same Gherkin syntax with its own step definition API. The implementation language matters less than the organization. Group step definitions by domain concept (user steps, payment steps, notification steps) rather than by feature file, so reuse happens naturally.

One pattern that scales well is the page object model for UI-level step definitions. Instead of putting Selenium or Playwright calls directly in step definitions, encapsulate page interactions in dedicated classes. The step definition calls loginPage.enterCredentials(email, password), and the page object handles the DOM interaction. When the UI changes, you update one page object instead of every step definition that touches the login form.

Where cucumber fits in the testing pyramid

Cucumber scenarios typically run at the acceptance test level, above unit tests and integration tests. They exercise complete user workflows through the application, which means they are slower and more brittle than lower-level tests. This is not a weakness if you calibrate expectations correctly.

The testing pyramid suggests having many unit tests, fewer integration tests, and even fewer end-to-end acceptance tests. Cucumber should cover your critical business paths: the workflows that generate revenue, handle sensitive data, or would cause significant damage if they broke. Trying to cover every edge case with cucumber scenarios leads to a slow, fragile suite that teams stop running.

A practical distribution for a SaaS product might look like 15 to 25 cucumber scenarios covering the core workflows (signup, subscription, primary feature usage, billing), with hundreds of unit tests covering the logic beneath those workflows. This gives you business-readable validation of the critical paths and fast, detailed feedback on the implementation details.

Understanding how these acceptance tests relate to the rest of your quality strategy is important. If you are building out your CI/CD pipeline and deciding which tests to run at which stage, cucumber scenarios typically belong in a post-deploy verification step rather than in the fast pre-merge check. They validate that the deployed system works, while unit tests validate that the code is correct before it merges.

Common pitfalls and how to avoid them

BDD with cucumber fails more often from process problems than from technical ones. Here are the patterns that reliably cause teams to abandon the approach, along with their remedies.

Writing scenarios after the code. When developers write Gherkin after implementing a feature, the scenarios describe what was built rather than what should have been built. This inverts the value proposition. Scenarios should be written during planning, ideally in a collaborative session, so they function as a shared specification. If your team writes scenarios retroactively, you are getting the maintenance cost of BDD without the communication benefit.

Making step definitions too specific. A step like "Given a user named John with email john@test.com created on January 5th with plan Premium" cannot be reused anywhere. Extract the essential information and parameterize the rest. "Given a user with a Premium plan" is reusable. The specific name and email can be generated or defaulted inside the step definition.

Ignoring failing scenarios. When a cucumber scenario fails intermittently, the temptation is to tag it as @wip or @skip and move on. This erosion compounds until a significant portion of your scenarios are disabled, at which point the suite provides no confidence and the team questions why they are maintaining it. Treat a failing scenario with the same urgency as a failing unit test. If it is flaky, fix the flakiness. If the behavior changed, update the scenario. If the scenario is no longer relevant, delete it.

Testing through the UI when an API exists. Browser-based cucumber tests are slow and fragile. If your application has an API, consider running scenarios against the API for business logic verification and reserving browser tests for the workflows where the UI itself is what you are testing. An API-level scenario that verifies order creation runs in milliseconds. The same scenario through a browser takes seconds and can fail because of a CSS animation timing issue.

Combining BDD with dedicated quality practices

Cucumber scenarios verify that specified behaviors work. By definition, they cannot verify unspecified behaviors, which are the scenarios nobody thought of during the planning session. This is where exploratory testing becomes essential. A human tester who understands the feature's intent can probe for the edge cases, workflow combinations, and user mistakes that no Gherkin scenario anticipated.

The combination is powerful. Cucumber gives you a repeatable baseline: every build confirms that the specified paths work. Exploratory testing gives you the discovery layer: each session uncovers new risks that can become new scenarios, expanding your automated coverage based on real findings rather than theoretical completeness.

Teams that run both practices consistently tend to have the lowest escaped defect rates, because they catch specification errors (through BDD collaboration), implementation errors (through automated scenarios), and design errors (through exploratory sessions). Each layer addresses a different failure mode, and none of them alone is sufficient.

If your team is practicing BDD but still finding that production bugs come from workflows your scenarios did not anticipate, the missing piece is usually that discovery layer. For a look at how teams add structured exploratory testing without building a QA department, see how managed QA integrates with existing engineering workflows.

Ready to level up your QA?

Book a free 30-minute call and see how Pinpoint plugs into your pipeline with zero overhead.