Definition / Implementation — Two-Level Test Scenarios

Azertio supports a two-level scenario model that separates what a test does from how it does it. The definition layer describes the test intent in high-level, business-readable language. The implementation layer provides the concrete, executable steps that fulfil that intent — potentially in a different natural language, with a different level of technical detail.

This mechanism is unique to Azertio and has no direct equivalent in other testing frameworks.


The Problem it Solves

In typical BDD projects, feature files serve two masters simultaneously:

These two needs often pull in opposite directions. Business-facing language is abstract and concise (Given a registered user), while executable steps tend to be technical and verbose (Given I send a POST to "/users" with body {"name":"Alice","email":"alice@example.com"}). The result is feature files that are too technical for business review, or steps so abstract they hide what actually happens — neither extreme is ideal.

Azertio solves this by letting you maintain both levels as separate files that are merged at plan-build time.


Core Concepts

Definition feature

A feature file tagged @definition declares the abstract structure of the test: the scenarios, their identifiers, and their high-level steps. These steps describe intent — they are not directly executable and do not need to match any step provider expression.

# language: en
@definition
Feature: User Registration

@ID-REG-01
Scenario: A new user can register with valid data
  Given a valid registration form
  When the form is submitted
  Then the account is created
  And a welcome email is sent

@ID-REG-02
Scenario: Registration fails with a duplicate email
  Given a registration form with an already-registered email
  When the form is submitted
  Then the account is not created
  And an error message is shown

This file is the contract between the business and the test team. It can be reviewed, challenged, and signed off by product owners without any technical knowledge.

Note: Background steps in definition features are intentionally ignored. The definition is a contract, not an execution plan; its background has no meaning at the implementation level.

Implementation feature

A feature file tagged @implementation provides the concrete steps for each scenario, matched by identifier. Implementation steps must match actual step provider expressions and will be executed by the framework.

# language: en
@implementation
Feature: User Registration — REST Implementation

Background:
  Given the base URL is "https://api.example.com"

@ID-REG-01
# gherkin.step-map: 1-1-1-1
Scenario: A new user can register with valid data
  When I make a POST request to "users" with body:
    """json
    { "name": "Alice", "email": "alice@example.com", "password": "s3cr3t" }
    """
  Then the HTTP status code is equal to 201
  And the response body contains:
    """json
    { "email": "alice@example.com" }
    """
  And the response body field "welcomeEmailSent" is equal to "true"

@ID-REG-02
# gherkin.step-map: 1-1-1-1
Scenario: Registration fails with a duplicate email
  When I make a POST request to "users" with body:
    """json
    { "name": "Bob", "email": "alice@example.com", "password": "s3cr3t" }
    """
  Then the HTTP status code is equal to 409
  And the response body contains:
    """json
    { "error": "EMAIL_ALREADY_EXISTS" }
    """
  And the response body field "message" contains "already registered"

The implementation Background is executed and is taken from the implementation feature.


How Matching Works

Scenarios are matched between definition and implementation by their identifier tag — the tag extracted by the pattern configured in core.idTagPattern (default: ID-(\w+)). A definition scenario with @ID-REG-01 is matched to the implementation scenario that also carries @ID-REG-01.

If a definition scenario has no identifier, it is silently dropped from the plan. If a definition scenario has an identifier but no matching implementation exists, it is also dropped (no failure — the scenario simply does not appear in the executed plan).


The Step Map

The gherkin.step-map property controls how implementation steps are distributed across the abstract definition steps. It is written as a Gherkin comment on the implementation scenario:

# gherkin.step-map: 2-1-2-0
@ID-1
Scenario: ...

The format is a dash-separated list of integers, one per definition step, indicating how many implementation steps replace each definition step:

Definition step Step-map value Result
Given two numbers 2 Replaced by 2 implementation steps
When they are multiplied 1 Replaced by 1 implementation step
Then the result is correct 2 Replaced by 2 implementation steps
And the world is wonderful 0 Becomes a virtual step (not executed)

A value of 0 marks the definition step as a virtual step: it appears in the result tree as a structural label but runs no code. This is useful when an abstract definition step has no direct technical equivalent — for example, a step that represents a business rule that is verified indirectly by other steps.

Default: one-to-one mapping

If gherkin.step-map is omitted, the framework assumes a 1-to-1 mapping: each definition step is replaced by exactly one implementation step. The total number of implementation steps must equal the number of definition steps in this case.


Cross-Language Scenarios

One of the most powerful applications of this mechanism is multilingual test suites. The definition is written in the business language of the project (English, Spanish, etc.), while each implementation can use the natural language that best fits the team executing it — or the compact DSL for brevity.

Definition (English — business language):

# language: en
@definition
Feature: Inventory Management

@ID-INV-01
Scenario: Stock is reduced after a sale
  Given a product with 10 units in stock
  When a sale of 3 units is recorded
  Then the remaining stock is 7 units

Implementation (Spanish — test team language):

# language: es
@implementation
Característica: Gestión de Inventario

# gherkin.step-map: 2-1-1
@ID-INV-01
Escenario: El stock disminuye tras una venta
  Dado que uso el origen de datos "inventory"
  Y la tabla products contiene las filas:
    | sku   | stock |
    | P-001 | 10    |
  Cuando realizo una petición POST a "sales" con cuerpo:
    """json
    { "sku": "P-001", "quantity": 3 }
    """
  Entonces el número de filas de la consulta es mayor que 0

The final executed plan shows the definition structure (English, business-readable) in the result tree, while executing the implementation steps (Spanish, technically precise). Stakeholders inspect the definition; the framework runs the implementation.


Scenario Outlines

For Scenario Outline, the mechanism works slightly differently:

This means business stakeholders control the data sets; the test team controls the execution steps.

Definition:

@definition
Feature: Pricing Rules

@ID-PRICE-01
Scenario Outline: Discount is applied correctly
  Given a product priced at <price>
  When a discount of <discount>% is applied
  Then the final price is <expected>
  Examples:
    | price | discount | expected |
    | 100   | 10       | 90       |
    | 200   | 25       | 150      |
    | 50    | 50       | 25       |

Implementation:

@implementation
Feature: Pricing Rules — REST

# gherkin.step-map: 1-1-1
@ID-PRICE-01
Scenario Outline: Discount is applied correctly
  When I make a POST request to "pricing/calculate" with body:
    """json
    { "price": <price>, "discountPercent": <discount> }
    """
  Then the HTTP status code is equal to 200
  Then the response body field "finalPrice" is equal to "<expected>"
  Examples:
    | price | discount | expected |
    | 999   | 99       | 9.99     |

The implementation Examples table is ignored. The plan is expanded using the definition's three rows.


Configuration

The tags used for definition and implementation are configurable in azertio.yaml:

configuration:
  core:
    definitionTag: definition          # default: "definition"
    implementationTag: implementation  # default: "implementation"
    idTagPattern: 'ID-(\w+)'           # default: "ID-(\\w+)"

If you want to use different tag names — for example @contract and @concrete — just change these values.


File Layout

A typical project using this mechanism looks like:

tests/
├── azertio.yaml
├── features/
│   ├── definition/
│   │   ├── user-registration.feature    (@definition)
│   │   ├── inventory.feature            (@definition)
│   │   └── pricing.feature              (@definition)
│   └── implementation/
│       ├── user-registration.feature    (@implementation)
│       ├── inventory.feature            (@implementation)
│       └── pricing.feature              (@implementation)

Both directories must be under the path configured in core.resourceFilter (default: **/*.feature).


What the Result Tree Shows

After the plan is built, definition/implementation tags are stripped from all nodes. The result tree shows:

This gives stakeholders a clean, business-readable view of results, while engineers can drill into each definition step to see exactly what concrete actions were taken.


When to Use This

Situation Recommendation
Single team, single language, technical audience Standard single-level scenarios — no need for definition/implementation
Business stakeholders actively review test scenarios Use definition/implementation to keep feature files business-readable
Same test contract, multiple protocol implementations One definition, multiple implementation files (e.g. REST vs gRPC)
Multilingual teams Definition in the project language, implementation in the team language
Regulatory or contractual test traceability Definition as the signed-off specification; implementation as the audit trail