Liking cljdoc? Tell your friends :D

Clornichon Feature Data Structure Documentation

This document describes the internal data structure of a Clornichon feature after parsing from Gherkin text. Understanding this structure is helpful when extending or customizing Clornichon.

Top-Level Structure

A feature is represented as a map with the following keys:

{:scenarios [...]       ; Vector of scenario maps
 :feature "..."         ; The Feature title
 :description "..."     ; Optional free text between the Feature line and the first keyword
 :annotations #{...}    ; Optional annotations (tags)
 :pre-run [...]         ; Hooks to execute before the feature
 :post-run [...]        ; Hooks to execute after the feature
 :messages [...]        ; The parser's Source, GherkinDocument and Pickle envelopes, for cucumber-messages
 :status :success/:fail ; Status after execution
 :exception ...         ; What a :pre-run or :post-run hook threw, if one did
}

Narrative

A narrative (As a … I want to … So that …) has no structure of its own: per the gherkin spec it is free text under the Feature line, so it lands in :description.

Annotations

Annotations (tags) are stored as a set of strings:

{:annotations #{"smoke" "regression" "api"}}

Scenarios

Each scenario is represented as a map within the :scenarios vector:

{:id "uuid-string"           ; The pickle's id
 :scenario-name "Name"       ; The scenario title
 :annotations #{...}         ; Optional annotations (tags) of the scenario
 :description "..."          ; Optional free text under the scenario line
 :rule {:id "..." :name "..." ; Only in a Rule: the rule grouping the scenario,
        :annotations #{...}   ; its own tags and description (the scenario
        :description "..."}   ; already inherits the tags in :annotations)
 :steps [...]                ; Vector of step maps
 :pre-run [...]              ; Hooks to run before the scenario
 :post-run [...]             ; Hooks to run after the scenario
 :default-state {}           ; Initial state for the scenario
 :status :success/:fail/:pending ; Execution status
 :exception ...              ; What a hook threw, if one did
 :started-at 1790511573279   ; Epoch millis, once run
 :finished-at 1790511573301
}

Steps Structure

Each step within a scenario is represented as a map:

{:id "uuid-string"                          ; The pickle step's id
 :sentence-keyword :given/:when/:then/:and  ; Step type
 :sentence "Step text"                      ; The actual step text
 :raw "Given Step text"                     ; Full text with keyword
 :order 0                                   ; Position in scenario
 :glue {...}                                ; Matched step definition
 :params [...]                              ; The glue's captures, then the datatable/docstring block
 :status :success/:fail/:pending            ; Execution status
 :input-state {}                            ; State before execution
 :output-state {}                           ; State after execution
 :exception {...}                           ; If step failed
 :started-at 1790511573279                  ; Epoch millis, once run
 :duration-ns 1234567                       ; Time the glue took, once run
}

Parameters

Parameters extracted from steps come in three types:

;; Value parameters (extracted from step text)
{:type :value, :val "some string"}
{:type :value, :val 42}

;; Table parameters
{:type :table,
 :val [{:header1 "value1", :header2 "value2"},
       {:header1 "value3", :header2 "value4"}]}

;; Doc string parameters (multi-line text blocks)
{:type :doc-string,
 :val "This is a multi-line\ntext block that can contain\nany content including markdown"}

;; A doc string opened with a content type (```json) carries it
{:type :doc-string, :val "{\"a\": 1}", :media-type "json"}

Hooks

Each hook of :pre-run / :post-run is the metadata of its var, plus:

{:ref #'user.namespace/clean-db!  ; The hook itself
 :arglists ([] ...)               ; From the var: a single one-argument arity receives the context
 :scenari/tags "@db"              ; Optional tag expression, from the var's metadata
 :tag-expr #object[...]           ; That expression, parsed when the feature is loaded
}

Glue Metadata

The :glue key contains information about the matched implementation function:

{:step "I do something {string}"  ; Pattern to match
 :ns user.namespace               ; Function namespace
 :name function-name              ; Function name
 :ref #'user.namespace/function   ; Reference to actual function
 :warning "Warning message"       ; Optional warning
}

Example Execution Flow

  1. Feature is parsed from text by io.cucumber/gherkin, into one pickle per runnable scenario
  2. Steps are matched to implementation functions via find-glue-by-step-regex
  3. During execution, each step receives the previous step's output state
  4. Parameters from the step text are extracted and passed to the implementation
  5. Function results and status are captured in the step's :output-state and :status
  6. Scenario status is derived from all contained steps' statuses
  7. Feature status is derived from all scenarios' statuses

Common Transformations

  • From Gherkin text → GherkinDocument + pickles, via io.cucumber/gherkin
  • From pickles → executable feature via ->feature-ast
  • Feature execution via run-feature
  • Step execution via run-step

This data structure provides a flexible representation that preserves all information from the original Gherkin text while supporting execution, reporting, and integration with test frameworks.

Constructs resolved at parse time

Background, Rule and Scenario Outline have no representation in this structure. Gherkin's pickle compiler resolves them away: a pickle is one runnable scenario, and the feature map is a thin translation of it.

  • Background — its steps are spliced at the head of every scenario's :steps (the feature's background first, then the enclosing rule's, if any).
  • Rule — only groups scenarios, so its scenarios are lifted into the feature's :scenarios, inheriting its tags. Each carries the rule under :rule, which the report and the Kaocha tree group them by.
  • Scenario Outline — becomes one scenario per Examples row, with the <placeholders> substituted throughout the scenario, its name included.

Can you improve this documentation? These fine people already did:
Hiram MADELAINE & davidpanza
Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close