Cucumber Architecture
View SourceThis document provides an overview of the Cucumber implementation architecture, explaining the core components and how they interact.
Core Components
Cucumber
├── Gherkin (Parser)
├── Expression (Parameter Matching)
├── Runner (Test Execution)
└── Formatter (Output)
Gherkin Parser
The Gherkin parser is responsible for parsing .feature
files into a structured format that can be executed. It handles the syntax of Gherkin, including:
- Feature declarations
- Scenario outlines
- Backgrounds
- Steps (Given, When, Then)
- Tables and doc strings
- Tags
The parser produces an Abstract Syntax Tree (AST) that represents the structure of the feature file.
# Simplified representation of the Gherkin parser flow
Feature File (Text) → Lexer → Tokens → Parser → AST
Expression Engine
The Expression engine is responsible for matching step text against step definitions. It supports:
- Regular expressions
- Cucumber expressions (a simplified syntax with parameter types)
- Parameter conversion (string to typed values)
defmodule Cucumber.Expression do
# Converts a cucumber expression into a regex and parameter converters
def compile(pattern) do
# Transforms {string}, {int}, etc. into regex patterns
# Returns {regex, converters}
end
# Matches text against a compiled expression
def match(text, {regex, converters}) do
# Returns {:match, args} or :no_match
end
end
Runner
The Runner is responsible for executing the parsed features against step definitions. It:
- Loads feature files
- Finds matching step definitions
- Executes steps in order
- Manages test context between steps
- Handles errors and reporting
Feature → Scenarios → Steps → Step Definitions → Execution
Context Management
The context is a map that's passed between step definitions, allowing them to share state. The runner:
- Creates an initial context
- Passes it to each step
- Collects the updated context
- Passes the updated context to the next step
# Example context flow
initial_context = %{feature_name: "Authentication"}
{:ok, updated_context} = execute_step(step1, initial_context)
{:ok, final_context} = execute_step(step2, updated_context)
Integration with ExUnit
The Cucumber library integrates with ExUnit through:
- Custom ExUnit case modules
- Test module generation
- Test callbacks (setup, teardown)
# Simplified representation of ExUnit integration
defmodule MyTest do
use Cucumber, feature: "my.feature"
# Step definitions become test functions
# Scenarios become test cases
end
Macro System
Cucumber uses Elixir's macro system extensively to provide a clean DSL:
defmodule MyTest do
use Cucumber, feature: "authentication.feature"
# Macros transform these into functions
defstep "I am on the login page", _context do
# Implementation
:ok
end
end
At compile time, these macros:
- Read the feature file
- Generate ExUnit test cases
- Register step definitions
- Set up test callbacks
Extension Points
The architecture provides several extension points:
- Custom Parameter Types: Extend the Expression engine with new types
- Formatters: Create custom output formats
- Hooks: Add before/after hooks at different levels
- Tags: Filter and customize execution based on tags
Implementation Details
Optimizations
- Step Definition Registry: Fast lookup of step definitions
- Pattern Compilation: Expressions are compiled once and reused
- Lazy Loading: Feature files are parsed on demand
Error Handling
When a step fails, the system:
- Captures the error information
- Adds context about the feature and scenario
- Reports detailed failure information
- Stops execution of the current scenario
- Continues with the next scenario
Execution Flow
1. Feature loading
├── Parse feature files
└── Build execution plan
2. Test compilation
├── Generate ExUnit tests
└── Register step definitions
3. Test execution
├── Setup test environment
├── Execute steps
│ ├── Find matching step definition
│ ├── Apply parameter conversions
│ ├── Execute step function
│ └── Manage context between steps
└── Report results
Code Structure
lib/
├── cucumber.ex # Main module and API
├── gherkin.ex # Feature file parser
├── cucumber/
├── expression.ex # Step matching engine
├── runner.ex # Test execution
├── formatter.ex # Output formatting
├── step_definition.ex # Step definition handling
└── hooks.ex # Before/after hooks
Runtime Behavior
- The
use Cucumber
macro transforms the module at compile time - ExUnit runs the generated test cases
- Each scenario becomes a test case
- Each step invocation:
- Finds the matching step definition
- Extracts parameters
- Runs the step function
- Manages the context
- Results are reported through ExUnit's reporting system
This architecture provides a solid foundation that balances simplicity of use with flexibility for extension.