Skip to main content

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 failing noScenariosInRule() test
  • Empty Scenarios (no steps) generate a failing Assertions.fail("Scenario has no steps") test
  • Both are tagged @new by 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​

  1. List Rules — write just the rule titles to outline the business domain.
  2. Compile — each empty rule becomes a failing @Test tagged @new.
  3. Add scenario titles under the first rule — still no steps.
  4. Compile — each empty scenario becomes a failing @Test tagged @new.
  5. Add steps to one scenario — it now generates step methods.
  6. Implement the step methods in a concrete subclass (ShoppingCartTest).
  7. Green — that scenario passes.
  8. 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​

OptionDefaultWhat it does
emptyScenarioBehaviorFAILFAIL, ABORT, or COMPILATION_ERROR for scenarios with no steps
emptyRuleBehaviorFAILFAIL, 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