Agile Testing & BDD
Most software teams today work in short cycles called sprints and release often. There is no long "testing phase" at the end; testing happens every day, by the whole team. Agile testing is the set of habits that makes this work, and behaviour-driven development (BDD) is a popular way to write requirements as examples that become automated tests.
In this lesson you will learn the tester's role in an agile team, the test pyramid and the agile testing quadrants, and how to write Gherkin scenarios and connect them to code.
Testing in an agile team
In agile, requirements arrive as user stories: short statements such as "As a shopper, I want free delivery on bigger orders, so that I save money when I buy more." Each story has acceptance criteria, the conditions it must meet to be accepted.
The tester's job changes from "checking at the end" to "helping build quality in from the start":
- Refinement. Testers ask questions while stories are written: "What about exactly ₹499? What if the coupon brings the total below ₹499?"
- Three amigos. A product owner, a developer and a tester discuss each story before coding: business, building and breaking viewpoints.
- During the sprint. Testers test each story as soon as it is ready, pair with developers and automate regression checks.
- Definition of done. The team agrees that a story is not done until its tests pass, its bugs are fixed and its automation is added.
This movement of testing towards the start of the work is called shift-left. Finding a gap in a story during refinement costs one conversation; finding it after release costs a hotfix.
The test pyramid
The test pyramid is a guide for balancing automated tests:
/\ UI / end-to-end: few, slow, realistic
/ \
/----\ Service / API / integration: some
/ \
/--------\ Unit: many, fast, cheapMost checks should be fast unit tests, a smaller number should test services and APIs, and only a few should drive the full user interface. A team that does the opposite, with hundreds of slow browser tests and few unit tests (sometimes called the "ice-cream cone"), gets slow, flaky pipelines.
The agile testing quadrants
The agile testing quadrants sort testing activities along two questions: does the test face the business or the technology, and does it guide development or critique the product?
| Guides development | Critiques the product | |
|---|---|---|
| Business-facing | Q2: story tests, examples, BDD scenarios, prototypes | Q3: exploratory testing, usability testing, user acceptance testing |
| Technology-facing | Q1: unit tests, component tests | Q4: performance, load, security and other non-functional tests |
The quadrants are not an order of work; a team uses all four in every release. They are a checklist that reminds you not to forget a whole kind of testing.
Behaviour-driven development
BDD grew out of test-driven development. The team describes each behaviour with concrete examples in a structured, plain-English language called Gherkin. The same text is a requirement the business can read, a test case a tester can follow, and an automated test a tool can run.
Feature: Delivery charge
As a shopper in Pune
I want free delivery on bigger orders
So that I save money when I buy more
Rule: Orders of 499 rupees or more get free delivery
Scenario: Order above the limit
Given my cart total is 520 rupees
When I go to checkout
Then the delivery charge is 0 rupees
Scenario Outline: Orders around the limit
Given my cart total is <total> rupees
When I go to checkout
Then the delivery charge is <charge> rupees
Examples:
| total | charge |
| 498 | 40 |
| 499 | 0 |The keywords:
- Feature: the capability, often with the user story underneath.
- Rule: one business rule the scenarios illustrate.
- Scenario: one concrete example.
- Given: the starting situation. When: the action. Then: the expected outcome. And and But continue the previous kind of step.
- Scenario Outline with Examples: the same steps run once per row, which is data-driven testing in Gherkin. Note how the examples use the boundary values 498 and 499.
How scenarios become tests
A BDD tool reads the feature file, matches each step's text against step definitions (small functions in code) and runs them in order. Here is the idea in a few lines of Python. The function under test has a boundary defect:
import re
feature = """
Scenario: Free delivery on bigger orders
Given my cart total is 520 rupees
When I go to checkout
Then the delivery charge is 0 rupees
Scenario: Exactly 499 rupees is free
Given my cart total is 499 rupees
When I go to checkout
Then the delivery charge is 0 rupees
"""
def delivery_charge(total):
return 0 if total > 499 else 40 # defect: rule says 499 or more
STEPS = []
def step(pattern):
def register(func):
STEPS.append((re.compile(pattern), func))
return func
return register
@step(r"my cart total is (\d+) rupees")
def cart_total(ctx, amount):
ctx["total"] = int(amount)
@step(r"I go to checkout")
def checkout(ctx):
ctx["charge"] = delivery_charge(ctx["total"])
@step(r"the delivery charge is (\d+) rupees")
def check_charge(ctx, amount):
assert ctx["charge"] == int(amount), f"got {ctx['charge']}"
def run(text):
ctx = {}
for line in text.strip().splitlines():
line = line.strip()
if not line:
continue
if line.startswith("Scenario:"):
ctx = {}
print(line)
continue
keyword, _, rest = line.partition(" ")
for pattern, func in STEPS:
match = pattern.fullmatch(rest)
if match:
try:
func(ctx, *match.groups())
print(f" passed {line}")
except AssertionError as error:
print(f" FAILED {line} ({error})")
break
else:
print(f" UNDEFINED {line}")
run(feature)Scenario: Free delivery on bigger orders passed Given my cart total is 520 rupees passed When I go to checkout passed Then the delivery charge is 0 rupees Scenario: Exactly 499 rupees is free passed Given my cart total is 499 rupees passed When I go to checkout FAILED Then the delivery charge is 0 rupees (got 40)
The @step(...) lines are decorators: each one registers the function below it with a text pattern. The numbers captured by (\d+) are passed to the function. A step with no matching definition is reported as undefined, which is how real tools tell you which glue code is still missing.
Real tools: behave, pytest-bdd and Cucumber
In Python, behave (version 1.3 at the time of writing) and pytest-bdd are the common choices. In JavaScript and Java, Cucumber is the best known. With behave, the feature file goes in a features folder and step definitions in features/steps:
# features/steps/delivery_steps.py
from behave import given, when, then
def delivery_charge(total):
return 0 if total >= 499 else 40
@given("my cart total is {total:d} rupees")
def step_cart_total(context, total):
context.total = total
@when("I go to checkout")
def step_checkout(context):
context.charge = delivery_charge(context.total)
@then("the delivery charge is {charge:d} rupees")
def step_check_charge(context, charge):
assert context.charge == charge, f"expected {charge}, got {context.charge}"{total:d} captures a whole number, and context is an object behave shares between the steps of one scenario. Install with python -m pip install behave and run behave in the project folder. Each step is printed with the file and line of its definition, and the run ends with a summary like this:
1 feature passed, 0 failed, 0 skipped 1 rule passed, 0 failed, 0 skipped 3 scenarios passed, 0 failed, 0 skipped 9 steps passed, 0 failed, 0 skipped
Common mistakes
- Writing UI clicks in Gherkin. "When I click the button with id btn-23" describes the screen, not the behaviour. Write "When I go to checkout"; the step definition handles the clicks.
- BDD without conversation. Feature files written alone by a tester after coding lose the main benefit: shared understanding before coding.
- Huge scenarios. Keep each scenario to one behaviour, usually 3 to 7 steps.
- An upside-down pyramid. Do not test calculations through the browser when a unit test can check them in milliseconds.
Key points
- In agile teams testing happens continuously and testers help shape stories from the start (shift-left).
- The test pyramid recommends many unit tests, fewer service tests and few UI tests.
- The agile testing quadrants remind teams to cover business-facing and technology-facing tests that both guide and critique.
- BDD describes behaviour with concrete Gherkin examples using Given, When and Then.
- Step definitions connect each Gherkin step to code; behave, pytest-bdd and Cucumber run them.
Exercise
Write a Gherkin feature for this rule from a movie ticket app in Kochi: "Students get 20% off tickets on weekdays, but not on weekends." Include a Scenario Outline with an Examples table covering a student on a weekday, a student on a weekend and a non-student on a weekday, for a ₹250 ticket.
Show solution
Feature: Student discount
As a student in Kochi
I want cheaper tickets on weekdays
So that I can watch more films
Rule: Students get 20% off on weekdays only
Scenario Outline: Ticket price by customer type and day
Given a ticket costs 250 rupees
And I am a <customer>
When I book a ticket for <day>
Then I pay <price> rupees
Examples:
| customer | day | price |
| student | Tuesday | 200 |
| student | Saturday | 250 |
| non-student | Tuesday | 250 |Each row is one example of the rule. The table makes it easy for the product owner to spot a missing case, such as a public holiday that falls on a weekday, and add a row before any code is written.
Quick Quiz
1. Which Gherkin keyword describes the action the user takes?
Given sets up the situation, When is the action, and Then is the expected outcome.
2. What does the test pyramid recommend?
Fast, cheap unit tests form the base; slow end-to-end tests are kept few.
3. Exploratory testing and user acceptance testing sit in which agile testing quadrant?
They judge the product from the business and user point of view.
4. What connects a Gherkin step to code in behave?
behave matches each step's text with the pattern in @given, @when or @then decorators.
Learn Software Testing with a mentor
Free lessons get you started. Our Software Testing training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
