Configuration model

<- Back to home

The config format is intentionally small: start with variables and objects, then add parsers or logging only if your suite really needs those extras.

Root structure

Key

Type

Purpose

version

int

Config format version. Defaults to 1.

variables

mapping

Reusable values referenced with $var in YAML or {{var:name}} in feature files.

objects

mapping

Named object definitions managed by the toolkit.

parsers

mapping

Optional Behave parser/type helpers configured at import time.

logging

mapping

Optional named logger definitions configured from before_all().

When to use each root section

Section

Use it when…

Typical status

variables

you want reusable literal values such as URLs, file names, or environment labels

common

objects

you want the toolkit to create, inject, and clean up instances for you

core feature

parsers

you already have custom Behave types and want to move matcher/type registration into YAML

optional

logging

you need several named loggers defined from config

optional

If you only want one persistent test-run.log, skip logging: and call configure_test_logging(...) yourself. That is the recommended starting point for most suites.

File or directory input

install(...) and configure_parsers(...) accept either:

  • one YAML file

  • or a dedicated config directory

When you pass a directory, behave-toolkit loads every .yaml and .yml file recursively in deterministic path order and merges the known root sections.

features/
  behave-toolkit/
    00-variables.yaml
    10-parsers.yaml
    20-objects.yaml
    30-logging.yaml

Duplicate names across files fail fast. Defining the same object, parser type, or logger twice is treated as a config error instead of silently letting the last file win.

Object fields

Each entry in objects supports the following fields:

Field

Required

Purpose

factory

yes

Import path for the callable used to create the object.

scope

no

One of global, feature, or scenario. Defaults to scenario.

args

no

Positional constructor arguments.

kwargs

no

Keyword constructor arguments.

cleanup

no

Method or attribute name called during cleanup.

inject_as

no

Context attribute name used instead of the object name.

factory can point to:

  • code from your own project

  • an installed dependency from the active environment

  • the Python standard library

Literal constructor values

Yes: args and kwargs accept direct YAML values out of the box.

Use $ref and $var only when you want indirection. If you just want to pass hard-coded values to the constructor, write them directly:

objects:
  api_client:
    factory: demo.clients.ApiClient
    args:
      - https://example.test
      - 30
    kwargs:
      verify_ssl: true
      headers:
        User-Agent: behave-toolkit-smoke

That is equivalent to:

ApiClient(
    "https://example.test",
    30,
    verify_ssl=True,
    headers={"User-Agent": "behave-toolkit-smoke"},
)

Supported direct values include normal YAML scalars, lists, and mappings. Only mappings that use $ref or $var are treated specially by the toolkit.

Scope dependency rules

Objects can depend on wider or same-scope objects, but not on narrower scopes.

Object scope

It may depend on…

global

other global objects and root variables

feature

global or feature objects, plus root variables

scenario

global, feature, or scenario objects, plus root variables

That means:

  • a scenario object can safely reuse a feature or global object

  • a feature object cannot depend on a scenario object

  • a global object cannot depend on feature or scenario objects

Context injection and naming

By default, each created object is exposed on the Behave context using its object name.

Example:

objects:
  report_path:
    factory: pathlib.Path
    args:
      - reports
      - latest.json

becomes context.report_path.

If you want a different name, use inject_as:

objects:
  report_path:
    factory: pathlib.Path
    inject_as: output_file
    args:
      - reports
      - latest.json

becomes context.output_file.

context.toolkit is reserved for the toolkit manager unless you install it under another namespace.

Markers

The config stays explicit by using dedicated markers instead of hidden magic.

Marker

Purpose

$ref

Reuse another configured object.

$ref + attr

Reuse one attribute path from another object.

$var

Reuse a root-level variable from the config.

Variables in YAML vs variables in feature files

behave-toolkit supports two different but related variable mechanisms:

Use case

Syntax

Where it is used

Extra helper required?

reuse a config value inside YAML object or logger fields

$var

YAML config

no

reuse a config value directly inside Gherkin text

{{var:name}}

parsed feature files

yes, call substitute_feature_variables(context)

Feature-file substitution is intentionally stricter than YAML substitution: {{var:name}} placeholders must resolve to scalar strings, numbers, or booleans.

Example with references and variables

version: 1
variables:
  report_name: report.json

objects:
  artifacts_path:
    factory: pathlib.Path
    scope: global
    args:
      - artifacts

  workspace:
    factory: tempfile.TemporaryDirectory
    scope: feature
    cleanup: cleanup

  workspace_path:
    factory: pathlib.Path
    scope: feature
    args:
      - $ref: workspace
        attr: name

  report_path:
    factory: pathlib.Path
    scope: scenario
    args:
      - $ref: workspace_path
      - $var: report_name

  latest_report_path:
    factory: pathlib.Path
    scope: global
    args:
      - $ref: artifacts_path
      - $var: report_name

Feature-file placeholders

If you want to reuse root variables directly in Gherkin, call substitute_feature_variables(context) from before_all() after install().

Scenario: Login against {{var:environment_name}}
  Given I open {{var:base_url}}

The helper currently rewrites:

  • feature, rule, background, and scenario names

  • description lines

  • step text

  • docstrings

  • table headings and cells, including Scenario Outline example tables

Tags are intentionally not rewritten.

Parser configuration

The optional parsers section configures Behave custom types and the default step matcher.

Field

Purpose

step_matcher

Default Behave matcher to use while step modules are imported.

types

Mapping of custom type names to parser helper specs.

Each custom type can either reference an existing converter or generate one from an enum:

Field

Purpose

converter

Import path to an existing converter callable.

enum

Import path to an Enum class. The toolkit builds the converter for you.

pattern

Regex pattern attached to the converter if it does not already define one.

regex_group_count

Optional regex group metadata for advanced parse patterns. Most users can omit it.

matcher

Per-type matcher override for registration (parse or cfparse).

case_sensitive

Enum helper option controlling case-sensitive lookup.

lookup

Enum helper mode: value or name.

See Parser helpers for end-to-end examples.

Logging configuration

If you only want one persistent test-run.log, you can skip logging: entirely and call configure_test_logging(...) from before_all().

Use the optional logging section when you want multiple named loggers to be materialized by configure_loggers(context).

logging:
  test_run:
    path:
      $ref: test_log_path
    logger_name: suite-tests
    inject_as: test_logger

  diagnostics:
    path:
      $ref: diagnostics_log_path
    logger_name: suite-diagnostics
    inject_as: diagnostics_logger
    console: false

Each logger supports:

Field

Purpose

path

File path or marker-based value used for the log file.

logger_name

Optional underlying Python logger name. Defaults to the logger key.

inject_as

Optional Behave context attribute name for the logger object.

level

Logging level passed to Python logging. Defaults to INFO.

console

Mirror output to console in addition to the file. Defaults to true.

mode

File open mode, for example w or a. Defaults to w.

Logger paths support the same marker style as object arguments.

Logger creation is intentionally global in this first version, so call configure_loggers(context) only after global objects are active. With the default install(context, CONFIG_PATH) flow that means immediately after install() is fine.

For the full logging story, see Logging and diagnostics.

Validation behavior

install() validates the whole config before any object is left attached to the Behave context. That catches problems early, including:

  • invalid scope values

  • non-importable factories

  • unknown $ref targets

  • unknown $var names

  • cycles between object references

  • invalid wider-to-narrower scope dependencies

  • conflicting context names for objects or loggers

substitute_feature_variables() then validates feature placeholders when you opt into that helper. Unknown names, circular variable references, and non-scalar resolved values fail fast with IntegrationError.

Tip

If a config change fails, read Troubleshooting next. It explains how to diagnose the most common validation and hook-order problems.