TDD Workflow
Iterative red-green development backed by failing tests generated from incomplete feature files.
Source: examples/common-use-cases/example-5 on GitHub.
What this demonstrates
- Empty
Rules (no scenarios) generate a failingnoScenariosInRule()test - Empty
Scenarios (no steps) generate a failingAssertions.fail("Scenario has no steps")test - Both are tagged
@newby default — easy to filter or display in your IDE - Scenarios with steps still generate normal step methods
- A single feature can mix complete and incomplete scenarios
The TDD iteration cycle
- List Rules — write just the rule titles to outline the business domain.
- Compile — each empty rule becomes a failing
@Testtagged@new. - Add scenario titles under the first rule — still no steps.
- Compile — each empty scenario becomes a failing
@Testtagged@new. - Add steps to one scenario — it now generates step methods.
- Implement the step methods in a concrete subclass (
ShoppingCartTest). - Green — that scenario passes.
- Repeat for the next scenario, then the next rule.
Generated output for a mixed feature
The example's feature lists the fully specified rule first, then the work still to do:
public abstract class ShoppingCartScenarios extends ShoppingCartFeature {
// Abstract step methods — only for the scenario that has steps
public abstract void myCartSubtotalIs$p1(Double p1);
public abstract void iApplyDiscountCode$p1(String p1);
public abstract void theCartSubtotalShouldBe$p1(Double p1);
@Nested
@DisplayName("Rule: Discount codes apply a percentage reduction")
public class Rule_1 {
// Scenario with steps — normal generated method calls
@Test
@DisplayName("Scenario: Apply a valid discount code")
public void rule_1_scenario_1() {
myCartSubtotalIs$p1(100.00);
iApplyDiscountCode$p1("SAVE10");
theCartSubtotalShouldBe$p1(90.00);
}
// Scenario with no steps — still failing
@Test
@Tag("new")
@DisplayName("Scenario: Reject an expired discount code")
public void rule_1_scenario_2() {
Assertions.fail("Scenario has no steps");
}
}
@Nested
@DisplayName("Rule: Free shipping applies to orders over 50 euros")
public class Rule_2 {
// Scenarios with no steps — fail immediately
@Test
@Tag("new")
@DisplayName("Scenario: Free shipping when subtotal exceeds threshold")
public void rule_2_scenario_1() {
Assertions.fail("Scenario has no steps");
}
// … rule_2_scenario_2 likewise
}
// Rule with no scenarios — fails immediately
@Nested
@Tag("new")
@DisplayName("Rule: Cannot checkout with an empty cart")
public class Rule_3 {
@Test
public void noScenariosInRule() {
Assertions.fail("Rule doesn't have any scenarios");
}
}
}
Implementing the one finished scenario
Only the fully specified scenario has steps, so only its three steps exist to implement:
public class ShoppingCartTest extends ShoppingCartScenarios {
private static final Map<String, Integer> ACTIVE_DISCOUNT_CODES = Map.of("SAVE10", 10);
private double subtotal;
@Override
public void myCartSubtotalIs$p1(Double amount) {
subtotal = amount;
}
@Override
public void iApplyDiscountCode$p1(String code) {
int percentage = ACTIVE_DISCOUNT_CODES.get(code);
subtotal = subtotal * (100 - percentage) / 100;
}
@Override
public void theCartSubtotalShouldBe$p1(Double expectedSubtotal) {
assertEquals(expectedSubtotal, subtotal, 0.001);
}
}
Running ShoppingCartTest gives one passing test and four failing ones tagged @new — the empty rule and the three
empty scenarios. That red list is the backlog for the next iterations.
Relevant options
| Option | Default | What it does |
|---|---|---|
emptyScenarioBehavior | FAIL | FAIL, ABORT, or COMPILATION_ERROR for scenarios with no steps |
emptyRuleBehavior | FAIL | FAIL, ABORT, or COMPILATION_ERROR for rules with no scenarios |
tagForEmptyScenarios | "new" | Tag added to empty scenarios (set to "" to disable) |
tagForEmptyRules | "new" | Tag added to empty rules (set to "" to disable) |
See Configuration for the full list.
Run it
cd examples/common-use-cases/example-5
mvn test