ATDD in Practice: Team Collaboration Patterns for Java Projects

ATDD in Practice: Team Collaboration Patterns for Java Projects

Acceptance Test-Driven Development fails more often from collaboration problems than technical ones. Teams adopt Cucumber or Concordion, write feature files, and then wonder why their acceptance tests don't prevent production bugs. The issue is usually process: the tests are written by developers alone, after the code, without the business conversations that give ATDD its value.

This guide focuses on the collaboration patterns that make ATDD work — and how to connect them to automation frameworks like Cucumber, Concordion, and Serenity.

The Core Problem ATDD Solves

Requirements misunderstandings are expensive. A developer builds the wrong thing, a tester finds it in QA, it goes back for rework, the sprint slips. Studies consistently show that bugs found during requirements are 10-100x cheaper to fix than bugs found after deployment.

ATDD catches misunderstandings earlier by making requirements concrete before development starts. The mechanism is simple: instead of writing "the discount system should handle edge cases correctly," you write out exactly what happens for each case, get everyone to agree, and then automate those cases as tests.

The Three Amigos Session

Before any code is written for a feature, convene three perspectives:

The business representative (product manager, analyst, or domain expert) explains what success looks like from a user and business perspective. They know why the feature exists but may not know all the edge cases.

The developer asks feasibility questions and raises technical constraints. "What happens if the external payment service times out?" The business often hasn't thought about this.

The tester systematically explores negative paths, boundary conditions, and unusual inputs. "What if the customer applies two discount codes? What if they have store credit and a coupon?"

The session runs 30-60 minutes. Output: a set of concrete examples covering the happy path, the main error scenarios, and the boundary conditions. Everyone signs off.

Specification by Example in Practice

Abstract requirements have multiple valid interpretations. Concrete examples have one.

Abstract: "The checkout process should apply loyalty points correctly."

Concrete:

Points balance Order total Points applied Cash charged
500 $50 500 (=$5) $45
200 $10 200 (=$2) $8
1000 $30 300 (=$3, capped at 10%) $27
0 $50 0 $50
500 $5 500 (=$5) $0

Now every edge case is explicit. What happens when points exceed the order total? The table shows it. What's the cap? 10%, shown in row 3. The developer implements exactly this. The tester automates exactly this. No ambiguity.

This technique — Specification by Example — is the analytical core of ATDD. The Three Amigos session is where you produce these examples collaboratively.

From Examples to Gherkin

Concrete examples map directly to Cucumber scenarios:

Feature: Loyalty Points at Checkout

  Scenario Outline: Apply loyalty points to order
    Given a customer has <points> loyalty points
    When they place an order totaling $<order_total>
    Then <applied> points should be applied ($<discount> discount)
    And the remaining charge should be $<charged>

    Examples:
      | points | order_total | applied | discount | charged |
      | 500    | 50          | 500     | 5.00     | 45.00   |
      | 200    | 10          | 200     | 2.00     | 8.00    |
      | 1000   | 30          | 300     | 3.00     | 27.00   |
      | 0      | 50          | 0       | 0.00     | 50.00   |
      | 500    | 5           | 500     | 5.00     | 0.00    |

The Examples table is your Three Amigos table, now executable. The step definitions connect it to your application code. When all rows pass, the feature is done.

When to Automate at Which Layer

Not all acceptance tests should be end-to-end browser tests. Match the test layer to the risk:

Service/API layer — for business rules. Fast, stable, catches logic errors without WebDriver overhead. Most acceptance criteria for business logic belong here.

@Test
void loyaltyPointsCapAt10PercentOfOrderTotal() {
    CheckoutService checkout = new CheckoutService();
    CheckoutResult result = checkout.process(
        new Order(30.00), new LoyaltyPoints(1000)
    );
    assertThat(result.pointsApplied()).isEqualTo(300);
    assertThat(result.discount()).isEqualByComparingTo("3.00");
}

Integration layer — for data flow through the full stack (service + database + external calls). Catches integration issues that unit tests miss.

UI layer — for critical user journeys only. A 5-scenario smoke suite covering the main flows is more maintainable than 200 UI acceptance tests.

The common mistake: automating everything through the UI because "acceptance tests should test what users see." This makes your test suite slow and fragile. Move business logic tests to the service layer.

Keeping Specifications Current

Specifications rot when:

  • Developers change behavior without updating feature files
  • Product managers update requirements without telling QA
  • Testers add test cases that conflict with existing scenarios

Prevention patterns:

Treat feature files as first-class code. They live in version control alongside source. Changes to business logic require changes to feature files in the same commit.

Failing tests block deployment. If a feature file scenario fails, the build fails. This creates immediate pressure to keep specifications current.

Review feature files in sprint demos. Before closing a story, confirm the feature file matches what was shipped. Product and QA both read it.

Use Serenity's living documentation. Publish Serenity HTML reports after every CI run. Product managers who read the reports will flag scenarios that no longer match what the product does.

Concordion for Non-Gherkin Teams

Some teams find Gherkin unnatural — the Given/When/Then structure feels forced for complex business rules. Concordion offers an alternative: prose specifications in HTML with embedded test instrumentation.

<h2>Loyalty Points at Checkout</h2>

<p>
  When a customer with
  <span concordion:set="#points">500</span> loyalty points
  places an order totaling $<span concordion:set="#total">50.00</span>,
  the checkout applies
  <span concordion:assertEquals="applyPoints(#points, #total).pointsUsed">500</span>
  points, giving a $<span concordion:assertEquals="applyPoints(#points, #total).discount">5.00</span>
  discount, and charges $<span concordion:assertEquals="applyPoints(#points, #total).charged">45.00</span>.
</p>

Business stakeholders can write this. The HTML is the spec. When the fixture class passes, the spec becomes a verified document.

For table-driven cases, Concordion's table syntax mirrors the Specification by Example format:

<table concordion:execute="#result = applyPoints(#points, #total)">
  <tr>
    <th concordion:set="#points">Points</th>
    <th concordion:set="#total">Order Total</th>
    <th concordion:assertEquals="#result.pointsUsed">Points Applied</th>
    <th concordion:assertEquals="#result.charged">Amount Charged</th>
  </tr>
  <tr><td>500</td><td>50.00</td><td>500</td><td>45.00</td></tr>
  <tr><td>1000</td><td>30.00</td><td>300</td><td>27.00</td></tr>
</table>

Serenity for Report Visibility

Whatever framework you use for acceptance tests — Cucumber, Concordion, or JUnit — add Serenity for reporting. The Maven plugin generates HTML reports that show:

  • Which features have passing tests, failing tests, or no tests
  • Step-level narrative for every scenario run
  • Screenshots at each UI step
  • Coverage across your requirements hierarchy

These reports are the artifact you share with stakeholders. They answer "is this feature working?" with evidence rather than assertion.

Connecting to Production

ATDD ensures features work when built. Production monitoring ensures they keep working after deployment. HelpMeTest runs your critical user flows on a continuous schedule in production — the same scenarios your Three Amigos sessions defined, running every few minutes rather than only during CI. When a deployment or infrastructure change breaks a previously-verified flow, you know immediately.

The workflow: Three Amigos produces examples → Cucumber/Concordion automates acceptance tests → CI runs tests on every push → HelpMeTest monitors production continuously.

Summary

ATDD works when the Three Amigos session actually happens — when business, development, and QA produce concrete examples together before development starts. The automation frameworks (Cucumber, Concordion, Serenity) are the easy part. The discipline is the collaboration: running the sessions, producing specific examples, and treating feature files as living documentation that must stay current with the code.

Read more

Start now free