liveBook · Manning

Outside-in development

As explained in BDD in Action, Second Edition

Outside-in development is an implementation approach that starts with externally visible behavior or acceptance outcomes and progressively develops the internal components and behavior needed to support them.

Suppose an acceptance criterion says a traveler should see the next train departures. Outside-in development starts with that expected result, then works toward the code needed to produce it. Rather than designing every internal component first, developers use the acceptance criterion to decide what behavior to specify and implement next. section 2.3.7 section 3.5.4

A practical progression is:

  • Start with a customer-facing acceptance criterion that states the expected outcome. section 2.3.7
  • Write developer-facing specifications as unit tests for the behavior needed to meet it. Use those tests to discover what a component needs from its collaborators. section 2.3.7 section 3.5.4
  • Implement enough behavior to pass the tests, adding tests for further cases and edge conditions. Avoid code that contributes nothing to the acceptance outcome. section 2.3.7 section 3.5.4

Discovering a timetable interface

To find the next departures, an itinerary service needs to know which train lines connect two stations and when trains leave on those lines. A unit test can first supply a dummy timetable with fixed times. That test exposes the two operations the service needs from the timetable; further tests check multiple departures and the case where no more trains are available. The timetable’s own behavior can then be developed through its tests. section 3.5.4

The same principle can shape acceptance-test automation. Name a task for the actor’s goal, not the particular clicks used to achieve it. A task that locates a registration form can navigate through the home page or go directly to the registration page without changing a test that depends only on that goal. section 15.1.4

Listing 15.4  Grouping alternative implementations of the same task
import { Task } from '@serenity-js/core';
import { Click, Navigate } from '@serenity-js/web';
 
export class LocateRegistrationForm {
    static viaHomePage = () =>
        Task.where(`#actor locates registration form via home page`,
            Navigate.to('/'),
            Click.on(Form.buttonCalled('Register')),
        );
 
    static viaDirectNavigation = () =>
        Task.where(`#actor registration form via direct navigation`,
            Navigate.to('/register'),
        );
}

The two Serenity/JS task implementations make the substitution concrete: each locates the same form by a different route. section 15.1.4

How BDD in Action, Second Edition covers it

How deep it goes
Works through unit tests, a dummy collaborator, interface discovery, and edge cases; also shows goal-based substitution of acceptance-test tasks.
Examples
Uses train-departure tests and a timetable interface, plus alternative navigation tasks for a registration form.
What it assumes
Builds on acceptance criteria and unit-testing practices rather than teaching either from zero.
What you can do after
Practice/perform it by deriving component tests and interfaces from acceptance criteria; apply it via Serenity/JS to make goal-named tasks substitutable.
How it connects
Connects behavior-driven development, acceptance criteria, executable specifications, unit testing, and test automation.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of BDD in Action, Second Edition. The section links point to the parts of the book it draws on.