15 Mise-en-scène patterns for layering the automation codebase
The chapter explains how to structure BDD automation code so it stays clear, reusable, and adaptable as a solution grows. Using the idea of “mise-en-scène,” it frames automation as the careful staging of scenario execution: step definitions should stay focused on testing intent, while the technical work of driving the application, handling infrastructure details, and managing cross-cutting concerns is pushed into separate layers. This layering becomes especially valuable when teams automate through outer interfaces such as the UI or REST API, where complexity and duplication tend to surface quickly.
The first major step is introducing test drivers, which encapsulate the mechanics of interacting with the system under test so that step definitions do not become cluttered with HTTP calls, browser operations, or direct service invocations. The chapter then refines that idea with an infrastructure layer that centralizes repeated technical plumbing, such as request construction, authentication headers, response parsing, and error handling. On top of that, test actions are used to represent executable scenario actions in a way that can uniformly support logging, timing, and other shared behavior, while still allowing flexible handling of both successful and failing outcomes.
The chapter also shows how to make automation more flexible by choosing between different drivers at runtime and by swapping real external dependencies for stubs when fast or reliable feedback is needed. This makes it possible to run the same scenarios through different interfaces, support both happy-path and negative scenarios cleanly, and keep the automation codebase organized around responsibilities rather than technologies. Finally, it relates these ideas to widely used approaches such as hexagonal architecture and screenplay, emphasizing that the same principles of separation of concerns, configurability, and reuse can support maintainable test automation across many kinds of systems.
Polymorphic driver interface with multiple implementations
Behavior of the application with a real external time service
Setting and retrieving the time from a stub service
In hexagonal architectures, external services connect to the domain logic through ports and adapters
The Screenplay pattern concepts are actors, abilities, tasks, interactions, and questions
Summary
- Automated tests can exercise application functionality through any interface that is available.
- To test the application through an outer interface, there needs to be a running instance, called an application host which can be externally hosted or self-hosted.
- Testing self-hosted applications is preferable, because the tests can be provided with a temporary, isolated execution environment and can be given access to the application’s memory.
- The Test Driver pattern encapsulates the technical details of how a component works (the automation concern), while providing an outcome-oriented interface to the step definition (the testing concern) that depends on that component.
- Many components will depend on the same technical infrastructure (e.g. HTTPS), so use the Infrastructure Layer pattern to encapsulate the technical details that are needed by each Test Driver.
- The development team will benefit from non-functional test automation features (such as test logging and timing) that cuts across all domains. The Test Action pattern supports centralized definition and maintenance of these test features.
- A test action is a specific executable step that is used to prepare, exercise or verify a particular aspect of the application.
- Any test action may have several possible outcomes, so use the Attempt Action pattern to separate the initiation of an action from the verification of the expected outcome.
- Most scenarios can be targeted at any of the available application interfaces (e.g. UI, REST, direct method call). To gain confidence that all interfaces are behaving as expected, use the Polymorphic Driver pattern to dynamically select the appropriate application interface to target.
- Some behaviors depend on resource intensive components or component states that are hard to achieve. Use the Stub Dependency pattern to provide stubbed test components that are cheaper to use and easier to force into the required state.
- The Hexagonal Architecture pattern (aka Ports and Adapters) is a widely documented pattern that encompasses the driver patterns (Test Driver, Infrastructure Layer, Polymorphic Driver) and the stubbing of dependencies (Stub Dependency).
- The Screenplay pattern is a widely discussed model that introduces five key concepts: Actors, Abilities, Tasks, Interactions, and Questions. These concepts map closely to the layering patterns described in this chapter.
FAQ
What is the main goal of the mise-en-scène patterns in this chapter?
They help structure the automation codebase so scenario automation stays clean, maintainable, and aligned with the team’s automation strategy. The patterns separate responsibilities such as testing concerns, automation mechanics, infrastructure details, and cross-cutting behaviors.
Why should BDD automation be driven through an outer interface like a UI or REST API?
Automating through outer interfaces increases confidence that the application works as it will be used from the outside, not just through direct internal calls. It can also reveal integration issues that inner-interface automation may miss.
What is the purpose of the Test Driver pattern?
The Test Driver pattern encapsulates technical automation details so step definitions can focus on testing decisions rather than on how the application is exercised. It improves reuse and reduces duplication in step definitions.
How does the Infrastructure Layer pattern improve maintainability?
It centralizes infrastructure-specific concerns such as HTTP handling, JSON serialization, authentication headers, and error parsing. This keeps driver classes simpler and makes it easier to update technical details in one place.
What problem does the Test Action pattern solve?
It separates the execution of a test action from cross-cutting concerns like logging, timing, and error handling. This makes test actions easier to reuse and gives the automation code a uniform way to execute actions.
Why is the Attempt Action pattern useful for negative scenarios?
Negative scenarios need to capture failures without failing immediately. Attempt Action lets the step definition inspect the result and decide whether the failure was expected, instead of relying on an exception to stop execution.
What is a Polymorphic Driver?
A Polymorphic Driver allows the same driver interface to be implemented by different automation technologies, such as REST API calls or direct service calls. The concrete implementation can be chosen at run time through configuration or tags.
When should a team use a Stub Dependency pattern?
Use it when an external dependency is slow, hard to control, or prevents reliable testing, such as time, databases, or external services. A stub provides predictable behavior and faster feedback while still allowing periodic runs against the real dependency.
How do the test, automation, and infrastructure layers relate to each other?
The test layer contains step definitions and other BDD glue code, the automation layer contains driver classes that execute actions, and the infrastructure layer contains helpers that handle technical communication details. Each layer depends on the one below it.
How are Hexagonal Architecture and Screenplay related to these patterns?
Both are broader industry patterns that address similar concerns. Hexagonal Architecture helps separate core logic from external dependencies, while Screenplay offers a higher-level model for actors, tasks, interactions, and questions that aligns well with layered BDD automation.
Effective Behavior-Driven Development ebook for free