Ilesh Darji


Component-Level BDD vs Integration BDD — And Why BDD at All?

Why BDD in the First Place?

Behaviour Driven Development (BDD) is not just a testing technique — it’s a communication framework. It bridges the gap between business stakeholders, developers, and QA by expressing requirements in plain English using a shared language called Gherkin.

The core question BDD answers is:

“Does the software do what the business actually needs it to do?”

Written as:

Given a user is on the login page
When they enter valid credentials
Then they should be redirected to the dashboard

This isn’t just a test — it’s living documentation that non-technical stakeholders can read and verify.

Popular tools that support this approach include Behave (Python), Cucumber (Java/JS), SpecFlow (.NET), Playwright with Cucumber, and Cypress with the cypress-cucumber-preprocessor.


The BDD Pyramid: Where Does Each Layer Live?

         ▲
        / \
       /   \   ← Integration BDD (E2E journeys, cross-service)
      /─────\
     /       \ ← Component BDD (isolated UI behaviour)
    /─────────\
   /           \ ← Unit (pure logic, no BDD needed typically)
  /─────────────\

Component-Level BDD

Component BDD tests the behaviour of a single UI component in isolation — without routing, APIs, or the full app stack.

When to use it:

  • A component has non-trivial conditional logic (show/hide, error states, loading states)
  • The component is shared across multiple features or teams
  • You want fast, focused feedback during development

Example — a SubmitButton component:

Feature: Submit Button

  Scenario: Button is disabled while form is invalid
    Given the form has missing required fields
    Then the submit button should be disabled

  Scenario: Button shows loading state after click
    Given the form is valid
    When the user clicks submit
    Then the button should display a loading spinner

This can be implemented with any component-level testing tool — Cypress Component Testing, Playwright’s experimental component mode, or Storybook with Testing Library:

// Playwright / Testing Library style
test('is disabled when form is invalid', async ({ mount }) => {
  const component = await mount(<SubmitButton isValid={false} />)
  await expect(component.locator('button')).toBeDisabled()
})
# Behave step definition style
@then('the submit button should be disabled')
def step_impl(context):
    button = context.browser.find_element(By.CSS_SELECTOR, 'button[type="submit"]')
    assert not button.is_enabled()

Benefits:

  • Runs in milliseconds — no server, no DB
  • Catches regressions at the source
  • Makes component contracts explicit

Integration-Level BDD

Integration BDD tests end-to-end user journeys across multiple components, services, and APIs working together.

When to use it:

  • Testing a complete user flow (sign up → onboard → purchase)
  • Validating third-party integrations (payment gateway, auth provider)
  • Acceptance criteria sign-off with stakeholders

Example — a checkout flow:

Feature: Checkout

  Scenario: Successful purchase with saved card
    Given the user has items in their basket
    And they have a saved payment method
    When they confirm the order
    Then an order confirmation email should be sent
    And the inventory count should decrease by 1

Tools like Playwright + Cucumber, Behave + Selenium, or Cypress + cucumber-preprocessor can drive these scenarios end-to-end.

Benefits:

  • Validates the full system behaves as expected
  • Stakeholders can read and approve scenarios
  • Acts as regression safety net for cross-service behaviour

Component BDD vs Integration BDD — At a Glance

Component BDD Integration BDD
Scope Single component Full user journey
Speed Fast (ms) Slower (seconds–minutes)
Dependencies None (mocked) Real services / APIs
Who writes it Dev + QA QA + BA + PO
Feedback loop Immediate Deferred
Tooling Cypress CT, Playwright CT, Storybook Playwright, Behave, Cypress, Selenium
Best for UI logic, edge cases Business flows, acceptance

The Mistake Most Teams Make

Teams often skip component-level BDD and write everything as integration tests. This leads to:

  • Slow pipelines — hundreds of E2E tests that take 30+ minutes
  • Flaky tests — network timeouts, race conditions, environment drift
  • Hard-to-debug failures — a failing E2E test could mean anything

The fix: Push behaviour verification down the pyramid. Test component behaviour at the component level, and reserve integration BDD for the journeys that genuinely require the full stack.


Practical Recommendation

  • Use component BDD for anything that can be verified in isolation — state changes, conditional rendering, user interactions on a single component
  • Use integration BDD for critical user journeys and stakeholder sign-off
  • Aim for a ratio of roughly 3:1 — three component/unit-level BDD scenarios for every one integration scenario

Tool Selection Guide

Need Recommended Tools
Python-based BDD Behave + Selenium or Playwright
JS/TS E2E BDD Playwright + Cucumber-js, Cypress + cucumber-preprocessor
.NET BDD SpecFlow + Playwright
Java BDD Cucumber-JVM + Selenium or Playwright
Component isolation Cypress Component Testing, Playwright CT, Storybook

Final Thought

BDD’s real value isn’t the syntax — it’s the conversation it forces. When you write a Given/When/Then scenario, you’re asking: what does this actually need to do, and how do we know it’s done?

Whether at the component or integration level, and regardless of which tool your team has adopted, that question is always worth asking.


Written by Ilesh Darji — QA Engineer & Contractor