Should I test that?

Should I use BDD?

Answer

Yes, use BDD for rules that customers see: agree on concrete examples with the product owner before coding, automate each example as a test through the API in CI, and add Cucumber's Gherkin files only when someone who does not write code reads them.

Verdict on the code under testYes

Why

Yes, use BDD for behaviour customers depend on, and keep its automated part cheap. My typical case is a product team building a web application from user stories. Blast radius is users and Change frequency is regularly, because customers see each rule and later stories change it. Detectability is eventually, because a broken rule rarely crashes, and Reversibility is with-effort, a hotfix plus a repair script. Test cost is moderate, about an hour to automate one example through the API, so rule R11 gives Test, and BDD takes each expected result from the product owner instead of the code.

When the decision changes
WhenDecisionWhy
The story sets a price, a discount or a refund amountTest mandatory: one example for each amount and boundary, reviewed by a second personBlast radius rises to money and Reversibility to costly, because a wrong charge ends in refunds
The story says who may see or edit a recordTest mandatory: one example for each role, run with records of two accountsBlast radius rises to safety-or-legal and Detectability to never, because a missing check raises no error
The story is a main flow that fails loudly, such as signing upTest minimally: one automated example of the sign-up flowDetectability moves to same-day, because a user who cannot sign up reports it
One function decides the story's rule, such as which tasks count as overdueTest: automate the examples as a table-driven unit test, without GherkinTest cost falls to trivial, but Detectability stays eventually, which keeps the decision at Test
Nobody can write the expected result as an example, such as a search ranking tuned by feelTest the ranking differently: track clicks on the top results and alert when they dropTest cost rises to heavy, because a useful test needs judged results, while Detectability stays eventually
The app is a side project that only you runDo not write scenarios for the side project; check each feature by handBlast radius falls to none, because you bear every failure alone

What breaks if you don't test

A story says "overdue tasks appear in the Today list", and nobody asks whether a task due at 18:00 is overdue at 09:00. The developer picks one reading, the product owner meant the other, and customers get a list that looks normal but holds the wrong tasks. Without an automated example, the next story that touches the list breaks the rule again and no check fails.

What you lose if you over-test

Each Gherkin sentence needs a step definition, and a reworded sentence breaks a test while the behaviour stays the same. In my suites a browser scenario took about eight seconds, so 400 of them add 53 minutes to every run on one worker. The Cucumber blog itself advises testing as little as possible through the user interface.

How to test

  1. Before coding a story, write concrete examples with the product owner, one per rule and per boundary, as the Cucumber BDD guide describes under discovery.
  2. Write each as Given, When and Then: starting data, one action, the expected result.
  3. Automate each example through the API with seeded data, in RSpec, pytest or JUnit, and run it in CI on every pull request.
  4. Add Gherkin files only when the product owner reads them, and keep browser scenarios to the few flows that prove the pages are wired together.

When the answer changes

  • An example moves money or decides who can see a record.
  • Nobody can state the expected result before seeing it, as with rankings.
  • You work alone, with nobody to agree examples with.

Real incident + Code example

The scenarios we stopped running

On a field-service scheduling product I worked on, every story became Cucumber scenarios driven through the browser. After a year, 640 scenarios ran for 55 minutes in a nightly job, and the product owner had stopped opening the feature files. Selectors broke weekly, so the team tagged failing scenarios @wip to keep the run green. One tagged scenario checked that a reassigned job left the first technician's day list. A new server-side cache was not cleared on reassignment, and for nine days two technicians sometimes drove to the same customer. We moved the examples to request specs, kept Given, When and Then as comments, and the run fell to four minutes:

# Agreed example: "A reassigned job leaves the first technician's day list."
RSpec.describe "Technician day list", type: :request do
  before { allow(Rails).to receive(:cache).and_return(ActiveSupport::Cache::MemoryStore.new) }

  it "drops a job after the dispatcher reassigns it" do
    # Given a job on 2 March assigned to Ana, with her list already cached
    ana, ben = create_list(:technician, 2)
    job = create(:job, technician: ana, date: "2026-03-02")
    get "/api/technicians/#{ana.id}/jobs?date=2026-03-02", headers: dispatcher_headers

    # When the dispatcher reassigns the job to Ben
    patch "/api/jobs/#{job.id}", params: { technician_id: ben.id }, headers: dispatcher_headers

    # Then Ana's list no longer shows it
    get "/api/technicians/#{ana.id}/jobs?date=2026-03-02", headers: dispatcher_headers
    expect(JSON.parse(response.body).map { |j| j["id"] }).not_to include(job.id)
  end
end

FAQ

Are RSpec and Cucumber really worth it?

RSpec is worth it for any Ruby application whose behaviour customers depend on, and Cucumber is worth its extra layer of step definitions only when a product owner or analyst reads or edits the feature files. When only developers read them, the same examples cost less as RSpec specs.

Should TDD and BDD be used together?

Yes, use TDD and BDD together as two loops: a failing BDD example through the API states what the story must do, and TDD drives the classes that make it pass. Add a unit test for the same rule only when it checks a boundary the example does not.

Should BDD be automated with unit tests, integration tests, or both?

Automate a BDD example as a unit test when one function decides the rule, and as an integration test through the API when the rule spans a request, a query and a response. Keep browser scenarios to the few flows that prove the pages are wired together.