API Reference

Complete auto-generated reference for every public module in behave-kit.

Public API

behave-kit — utilities for robust Behave test suites.

Three adoption levels:

  1. Automatic wiringbehave_kit.setup(context) in before_all.

  2. Cherry-pickfrom behave_kit.assertions import assert_soft.

  3. Namespaceimport behave_kit as bk; bk.assert_soft(...).

class behave_kit.CompareOptions(ignore_keys: frozenset[str] = frozenset({}), float_tolerance: float = 1e-09, ignore_order: bool = False, datetime_tolerance: ~datetime.timedelta | None = None, custom_matchers: dict[type, ~collections.abc.Callable[[~typing.Any, ~typing.Any], bool]] = <factory>)[source]

Bases: object

Tunable behavior for deep_compare.

custom_matchers: dict[type, Callable[[Any, Any], bool]]
datetime_tolerance: timedelta | None = None
float_tolerance: float = 1e-09
ignore_keys: frozenset[str] = frozenset({})
ignore_order: bool = False
class behave_kit.DataCache[source]

Bases: object

Cache of loaded data, keyed by (path, scope).

get(path: str | Path, scope: Scope = Scope.SCENARIO) object | None[source]

Return the cached data for path at scope, or None.

Parameters:
  • path – File path or identifier used as the cache key.

  • scope – Scope whose cached value should be returned.

invalidate(scope: Scope) None[source]

Remove every entry cached at scope.

set(path: str | Path, scope: Scope, data: object) None[source]

Store data for path under scope.

Parameters:
  • path – File path or identifier used as the cache key.

  • scope – Scope at which the data should be cached.

  • data – Value to cache.

class behave_kit.KitConfig(env: str, base_url: str = '', browser: str = '', timeouts: Mapping[str, int]=<factory>, credentials: Mapping[str, str]=<factory>, raw: Mapping[str, ~typing.Any]=<factory>)[source]

Bases: object

Read-only, deeply immutable resolved configuration.

base_url: str = ''
browser: str = ''
credentials: Mapping[str, str]
env: str
classmethod from_toml(path: str | Path, *, env: str | None = None, overrides: dict[str, str] | None = None) KitConfig[source]

Load a KitConfig from a TOML file.

env defaults to behave_kit.env if omitted. CLI --set overrides are dotted keys flattened to top-level strings.

Raises:

ConfigError – if the file is missing, unreadable, malformed, or the requested profile does not exist.

get(key: str, default: Any = None) Any[source]

Return the raw configuration value for key or default.

raw: Mapping[str, Any]
timeouts: Mapping[str, int]
class behave_kit.Scope(*values)[source]

Bases: Enum

Lifetime of a registered item (fixture, scoped attribute, cached data).

FEATURE = 'feature'
GLOBAL = 'global'
SCENARIO = 'scenario'
exception behave_kit.TimeoutExceededError(message: str, *, cause: BaseException | None = None, suggestion: str | None = None)[source]

Bases: BehaveKitError

Raised when a callable takes longer than the allowed time.

class behave_kit.TypedContext(context: Context, schema: type[T])[source]

Bases: Generic[T]

Schema-validated proxy over a Behave Context.

setup(**kwargs: Any) None[source]

Type-checked assignment of attributes to the real context.

behave_kit.assert_dict_contains(d: Mapping[Any, Any], subset: Mapping[Any, Any], *, msg: str = '') None[source]

Assert that d contains every key/value pair present in subset.

behave_kit.assert_json_equals(actual: object, expected: object, *, msg: str = '', options: CompareOptions | None = None) None[source]

Assert that actual deep-equals expected, showing which keys differ.

behave_kit.assert_list_ordered(lst: Sequence[Any], *, key: Callable[[Any], Any] | None = None, msg: str = '') None[source]

Assert that lst is sorted, optionally by a key function.

behave_kit.assert_soft(condition: bool, msg: str = '') None[source]

Record a soft assertion failure if condition is falsy.

behave_kit.assert_soft_equals(actual: object, expected: object, msg: str = '') None[source]

Record a soft assertion failure if actual != expected.

behave_kit.assert_soft_is_none(value: object, msg: str = '') None[source]

Record a soft assertion failure if value is not None.

behave_kit.assert_soft_raises(expected_exception: type[BaseException] | tuple[type[BaseException], ...], func: Callable[[], object], msg: str = '') None[source]

Record a soft assertion failure if func does not raise expected_exception.

behave_kit.assert_soft_true(condition: object, msg: str = '') None[source]

Record a soft assertion failure if condition is not truthy.

behave_kit.assert_table_equals(actual: _TableLike, expected: _TableLike, *, msg: str = '') None[source]

Assert that two Behave Data Tables have the same headings and rows.

behave_kit.assert_under(seconds: float, func: Callable[[], R], *, message: str = '') R[source]

Run func and assert it completes within seconds.

Parameters:
  • seconds – Maximum allowed execution time.

  • func – Zero-argument callable to time.

  • message – Custom message for the timeout error.

Returns:

The return value of func.

Raises:

TimeoutExceededError – If func takes longer than seconds.

behave_kit.cleanup_scoped(context: Context, scope: Scope = Scope.SCENARIO) None[source]

Delete every attribute tracked with @scoped at the given scope.

behave_kit.continue_after_failed(enabled: bool = True) None[source]

Set whether scenarios continue executing after a failed step.

Sets the class-level attribute Scenario.continue_after_failed_step which Behave checks before aborting a scenario on step failure.

Parameters:

enabled – When True, scenarios keep running remaining steps even after a step fails. When False, execution stops at the first failing step (Behave’s default).

Raises:

BehaveKitError – If enabled is not a boolean.

behave_kit.continue_on_failure() Iterator[None][source]

Temporarily enable continue-after-failed for the current block.

Sets Scenario.continue_after_failed_step to True on entry and restores the previous value on exit, even if an exception occurs.

Usage:

with continue_on_failure():
    # any scenario that runs while inside this block
    # will continue after failed steps
    ...
behave_kit.convert(name: str, match_string: str) Any[source]

Apply the converter registered under name to match_string.

behave_kit.data_driven(path: str | Path) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Decorator: run the step once per row in the data file at path.

Each row (a dict) is unpacked as keyword arguments into the step function. Column names with hyphens or spaces are converted to valid Python identifiers (- and `` `` → _).

Usage:

@data_driven("tests/data/users.csv")
@when("I login as {username}")
def step(context, username=None, password=None): ...
Raises:

BehaveKitError – If the data file cannot be loaded or a row is not a dict.

behave_kit.data_provider(name: str) Callable[[Callable[[...], object]], Callable[[...], object]][source]

Register func as the data provider named name.

behave_kit.deep_compare(actual: object, expected: object, options: CompareOptions | None = None) DiffResult[source]

Recursively compare actual to expected and report all differences.

behave_kit.dump_context(context: Context, path: str | Path = 'debug/') Path[source]

Write every JSON-serializable attribute of context to a file.

Non-serializable attributes are skipped with a logging.warning.

Returns:

The path of the written file.

behave_kit.dump_context_on_failure(context: Context, scenario: object, path: str | Path = 'debug/') Path | None[source]

Call dump_context only when scenario.status is "failed".

behave_kit.env(key: str, *, required: Literal[True] = True, var_type: type[T] = <class 'str'>, default: Any = None, context: Context | None = None) T[source]
behave_kit.env(key: str, *, required: ~typing.Literal[False], var_type: type[~behave_kit.env.variables.T] = <class 'str'>, default: ~typing.Any = None, context: ~behave.runner.Context | None = None) T | None

Read environment variable key, converted to var_type.

Resolution order: os.environ -> context.config.raw (if context is given) -> default -> EnvVarError (if required).

behave_kit.env_snapshot() Iterator[None][source]

Snapshot os.environ and restore it on exit.

Any additions, modifications, or deletions made to environment variables inside the block are reverted when the block exits — even if an exception is raised.

behave_kit.fixture(name: str, scope: Scope = Scope.SCENARIO, requires: str | list[str] | None = None) Callable[[Callable[[Context], tuple[Callable[[Context], None], Callable[[Context], None]] | None]], Callable[[Context], tuple[Callable[[Context], None], Callable[[Context], None]] | None]][source]

Register func as the fixture named name.

Parameters:
  • name – Fixture name (matches a Behave tag).

  • scope – Lifetime of the fixture (scenario or feature).

  • requires – Other fixture names this one depends on. Accepts a single string or a list of strings.

behave_kit.get_path(data: object, path: str, default: Any = <object object>) Any[source]

Return the value at path within nested data, or default.

path uses dot notation to traverse nested dicts:

get_path(response, “user.address.city”)

Numeric segments traverse lists:

get_path(response, “users.0.name”)

Parameters:
  • data – The root dict or list to traverse.

  • path – Dot-separated path string.

  • default – Value to return when the path does not exist. If not provided, BehaveKitError is raised instead.

Raises:

BehaveKitError – When the path is not found and no default is given.

behave_kit.get_provider(name: str) Callable[[...], object][source]

Return the provider function registered under name.

behave_kit.load_data(path: str | Path) dict[str, Any] | list[Any][source]

Load path and return its parsed content, dispatched by extension.

Raises:

DataLoadError – if the file is missing, the extension is unsupported, or a required optional dependency (pyyaml, openpyxl) is not installed.

behave_kit.load_examples_from(path: str | Path) list[dict[str, Any]][source]

Load path and normalize the result to a list of dicts.

behave_kit.parameter_type(name: str, pattern: str = '') Callable[[Callable[[str], Any]], Callable[[str], Any]][source]

Register func as the converter for parameter type name.

pattern documents the expected input shape; matching the pattern against step text is left to the caller (Cucumber Expressions, parse, or a manual regex) – this decorator only registers the conversion.

behave_kit.register_builtin_types() None[source]

Register every built-in converter. Safe to call more than once.

behave_kit.run_steps(context: Context, steps: str) None[source]

Execute Gherkin sub-steps with state isolation.

Wraps context.execute_steps() with:

  • Scenario Outline <placeholder> substitution from context.active_outline.

  • Guaranteed restoration of context.table and context.text.

Parameters:
  • context – The Behave context for the current scenario.

  • steps – Gherkin step text to execute. Must be a string containing one or more step definitions with keywords (Given/When/Then/And/But).

Raises:
behave_kit.scoped(name: str, scope: Scope = Scope.SCENARIO) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Decorator: track context.<name> so it is cleaned up automatically.

After the wrapped step function runs, name is registered for cleanup at the given scope (see cleanup_scoped).

behave_kit.setup(context: Context, *, env: str | None = None, config_file: str = 'behave.toml', log_level: str = 'INFO', continue_after_failed: bool | None = None) None[source]

Wire all behave-kit modules into context.

Idempotent: calling twice is a no-op. Each module is wired independently in try/except — a failure in one does not prevent the others.

Parameters:
  • context – The Behave context object.

  • env – Optional environment name for profile-based configuration.

  • config_file – Path to the configuration file (default behave.toml).

  • log_level – Logging level for the behave_kit logger.

  • continue_after_failed – When True, scenarios continue executing remaining steps after a failure. When False, the default Behave behaviour (stop on first failure) is restored. None leaves the current setting unchanged.

behave_kit.setup_suggestions(context: Context) Callable[[Context, object], None][source]

Return an after_step(context, step) hook that logs suggestions.

Wire it in environment.py:

_suggest = behave_kit.steps.setup_suggestions(context)

def after_step(context, step):
    _suggest(context, step)
behave_kit.skip_if_env(env_name: str) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Skip the step when context.config.env == env_name.

behave_kit.skip_if_missing(module_name: str) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Skip the step when module_name cannot be imported.

behave_kit.skip_if_no_browser(func: Callable[[Concatenate[Context, P]], R] | None = None) Callable[[Concatenate[Context, P]], R] | Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Skip the step when Selenium is not installed.

Can be applied directly (@skip_if_no_browser) or called as a decorator factory (@skip_if_no_browser()).

behave_kit.skip_on_os(os_name: str) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Skip the step when running on os_name.

behave_kit.soft_asserts() Iterator[SoftAssertCollector][source]

Context manager for soft assertions outside of Behave (e.g. unit tests).

Raises an AssertionError summarizing every failure when the block exits without an exception of its own.

behave_kit.step_impl_base(default_matcher: Any = None) type[source]

Return a new base class for class-based step implementations.

Each call creates an isolated local step registry, so independent step libraries can coexist without interfering with each other. Subclasses share the registry of the base they derive from, which is what enables extension via subclassing and method overriding.

Parameters:

default_matcher – Default matcher for steps that don’t specify one. Accepts None (Behave’s current default), a string name ("parse", "re", "cfparse", "simplified", "cucumber"), or a Matcher subclass.

Returns:

A base class with given, when, then, step decorator factories and register() / clear() methods.

Raises:

StepError – If default_matcher is not a valid matcher reference.

behave_kit.teardown(context: Context) None[source]

Clean up wired modules in reverse order.

Only modules that were successfully wired during setup() are torn down. Safe to call without a prior setup() (no-op). Also tears down any class-based step instances created during the scenario, even if setup() was not called.

behave_kit.teardown_steps(context: Context) None[source]

Call teardown() on every live step-instance for context and clear them.

Safe to call when no class-based steps have run (no-op). Wired automatically by behave_kit.setup() / behave_kit.teardown().

Iterates over a snapshot of the instances dict so that a teardown() method that triggers additional step execution (which could add new instances) does not cause RuntimeError: dictionary changed size during iteration.

behave_kit.temp_workspace(*, prefix: str = 'behave_kit_') Iterator[Path][source]

Yield a temporary directory and restore the CWD on exit.

Parameters:

prefix – Prefix for the temporary directory name.

Yields:

Path to the temporary directory (the CWD while inside the block).

behave_kit.timed(seconds: float) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Decorator: assert a step function completes within seconds.

Usage:

@timed(2.0)
@when("I fetch the data")
def step(context): ...
Raises:

BehaveKitError – If seconds is negative.

behave_kit.use_soft_asserts(context: Context) SoftAssertCollector[source]

Activate soft assertions for the given Behave context.

Attaches a fresh SoftAssertCollector to context._behave_kit_soft and makes it the active collector for assert_soft and friends.

behave_kit.wait_until(condition: Callable[[], object], *, timeout: float = 10.0, interval: float = 0.5, message: str = '') None[source]

Poll condition until it is truthy or timeout seconds elapse.

Parameters:
  • condition – Zero-argument callable whose return value is evaluated with scalar-bool coercion (tolerates array-like objects).

  • timeout – Maximum number of seconds to wait (default 10).

  • interval – Seconds between polls (default 0.5).

  • message – Custom message for the timeout error.

Raises:

TimeoutError – If condition does not become truthy within timeout seconds.

behave_kit.when_if(condition: Callable[[Context], object]) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Decorator: execute the step only if condition(context) is true.

When the condition is false, the step is skipped (via unittest.SkipTest) rather than executed or failed.

Assertions

Assertion utilities: soft assertions and diff-based comparisons.

Pure comparison logic (_matchers) is decoupled from the Behave-facing API (diff, soft), which is decoupled from the failure formatting (reporter).

class behave_kit.assertions.CompareOptions(ignore_keys: frozenset[str] = frozenset({}), float_tolerance: float = 1e-09, ignore_order: bool = False, datetime_tolerance: ~datetime.timedelta | None = None, custom_matchers: dict[type, ~collections.abc.Callable[[~typing.Any, ~typing.Any], bool]] = <factory>)[source]

Bases: object

Tunable behavior for deep_compare.

custom_matchers: dict[type, Callable[[Any, Any], bool]]
datetime_tolerance: timedelta | None = None
float_tolerance: float = 1e-09
ignore_keys: frozenset[str] = frozenset({})
ignore_order: bool = False
class behave_kit.assertions.Diff(path: str, expected: object, actual: object, message: str)[source]

Bases: object

A single difference found while comparing actual to expected.

actual: object
expected: object
message: str
path: str
class behave_kit.assertions.DiffResult(equal: bool, diffs: list[Diff] = <factory>)[source]

Bases: object

Outcome of a deep_compare call.

diffs: list[Diff]
equal: bool
class behave_kit.assertions.SoftAssertCollector[source]

Bases: object

Accumulates assertion failures without raising immediately.

assert_soft(condition: bool, msg: str = '') None[source]

Record a failure if condition is falsy.

Parameters:
  • condition – Boolean (or bool-coercible) value to check.

  • msg – Optional message describing the failure.

assert_soft_equals(actual: object, expected: object, msg: str = '') None[source]

Record a failure if actual is not equal to expected.

Parameters:
  • actual – The value produced by the code under test.

  • expected – The value actual is expected to match.

  • msg – Optional override message for the failure.

assert_soft_is_none(value: object, msg: str = '') None[source]

Record a failure if value is not None.

Parameters:
  • value – The value to check.

  • msg – Optional override message for the failure.

assert_soft_raises(expected_exception: type[BaseException] | tuple[type[BaseException], ...], func: Callable[[], object], msg: str = '') None[source]

Record a failure if func does not raise expected_exception.

Parameters:
  • expected_exception – Exception type (or tuple of types) that func is expected to raise.

  • func – Zero-argument callable to invoke.

  • msg – Optional override message for the failure.

assert_soft_true(condition: object, msg: str = '') None[source]

Record a failure if condition is not truthy.

Parameters:
  • condition – Value evaluated with scalar-bool coercion.

  • msg – Optional override message for the failure.

clear() None[source]

Remove every recorded failure.

property failures: list[SoftFailure]

Copy of the recorded soft assertion failures.

raise_if_failed() None[source]

Raise AssertionError if any failure has been recorded.

report() SoftAssertReport[source]

Return an immutable report of all recorded failures.

class behave_kit.assertions.SoftAssertReport(failures: list[SoftFailure] = <factory>)[source]

Bases: object

Aggregated report of every soft assertion failure in a scenario.

property failure_count: int

Number of recorded soft assertion failures.

failures: list[SoftFailure]
property has_failures: bool

Whether any soft assertion failure has been recorded.

class behave_kit.assertions.SoftFailure(message: str, expected: object = <object object>, actual: object = <object object>)[source]

Bases: object

A single soft assertion failure.

actual: object = <object object>
expected: object = <object object>
property has_values: bool

Whether at least one of expected or actual was supplied.

message: str
behave_kit.assertions.assert_dict_contains(d: Mapping[Any, Any], subset: Mapping[Any, Any], *, msg: str = '') None[source]

Assert that d contains every key/value pair present in subset.

behave_kit.assertions.assert_json_equals(actual: object, expected: object, *, msg: str = '', options: CompareOptions | None = None) None[source]

Assert that actual deep-equals expected, showing which keys differ.

behave_kit.assertions.assert_list_ordered(lst: Sequence[Any], *, key: Callable[[Any], Any] | None = None, msg: str = '') None[source]

Assert that lst is sorted, optionally by a key function.

behave_kit.assertions.assert_soft(condition: bool, msg: str = '') None[source]

Record a soft assertion failure if condition is falsy.

behave_kit.assertions.assert_soft_equals(actual: object, expected: object, msg: str = '') None[source]

Record a soft assertion failure if actual != expected.

behave_kit.assertions.assert_soft_is_none(value: object, msg: str = '') None[source]

Record a soft assertion failure if value is not None.

behave_kit.assertions.assert_soft_raises(expected_exception: type[BaseException] | tuple[type[BaseException], ...], func: Callable[[], object], msg: str = '') None[source]

Record a soft assertion failure if func does not raise expected_exception.

behave_kit.assertions.assert_soft_true(condition: object, msg: str = '') None[source]

Record a soft assertion failure if condition is not truthy.

behave_kit.assertions.assert_table_equals(actual: _TableLike, expected: _TableLike, *, msg: str = '') None[source]

Assert that two Behave Data Tables have the same headings and rows.

behave_kit.assertions.deep_compare(actual: object, expected: object, options: CompareOptions | None = None) DiffResult[source]

Recursively compare actual to expected and report all differences.

behave_kit.assertions.soft_asserts() Iterator[SoftAssertCollector][source]

Context manager for soft assertions outside of Behave (e.g. unit tests).

Raises an AssertionError summarizing every failure when the block exits without an exception of its own.

behave_kit.assertions.use_soft_asserts(context: Context) SoftAssertCollector[source]

Activate soft assertions for the given Behave context.

Attaches a fresh SoftAssertCollector to context._behave_kit_soft and makes it the active collector for assert_soft and friends.

Soft assertions: collect failures instead of stopping at the first one.

The active SoftAssertCollector is resolved through contextvars rather than a module-level global, so it is thread-safe and naturally scoped per scenario (Behave hooks reset it before/after each scenario).

class behave_kit.assertions.soft.SoftAssertCollector[source]

Bases: object

Accumulates assertion failures without raising immediately.

assert_soft(condition: bool, msg: str = '') None[source]

Record a failure if condition is falsy.

Parameters:
  • condition – Boolean (or bool-coercible) value to check.

  • msg – Optional message describing the failure.

assert_soft_equals(actual: object, expected: object, msg: str = '') None[source]

Record a failure if actual is not equal to expected.

Parameters:
  • actual – The value produced by the code under test.

  • expected – The value actual is expected to match.

  • msg – Optional override message for the failure.

assert_soft_is_none(value: object, msg: str = '') None[source]

Record a failure if value is not None.

Parameters:
  • value – The value to check.

  • msg – Optional override message for the failure.

assert_soft_raises(expected_exception: type[BaseException] | tuple[type[BaseException], ...], func: Callable[[], object], msg: str = '') None[source]

Record a failure if func does not raise expected_exception.

Parameters:
  • expected_exception – Exception type (or tuple of types) that func is expected to raise.

  • func – Zero-argument callable to invoke.

  • msg – Optional override message for the failure.

assert_soft_true(condition: object, msg: str = '') None[source]

Record a failure if condition is not truthy.

Parameters:
  • condition – Value evaluated with scalar-bool coercion.

  • msg – Optional override message for the failure.

clear() None[source]

Remove every recorded failure.

property failures: list[SoftFailure]

Copy of the recorded soft assertion failures.

raise_if_failed() None[source]

Raise AssertionError if any failure has been recorded.

report() SoftAssertReport[source]

Return an immutable report of all recorded failures.

behave_kit.assertions.soft.assert_soft(condition: bool, msg: str = '') None[source]

Record a soft assertion failure if condition is falsy.

behave_kit.assertions.soft.assert_soft_equals(actual: object, expected: object, msg: str = '') None[source]

Record a soft assertion failure if actual != expected.

behave_kit.assertions.soft.assert_soft_is_none(value: object, msg: str = '') None[source]

Record a soft assertion failure if value is not None.

behave_kit.assertions.soft.assert_soft_raises(expected_exception: type[BaseException] | tuple[type[BaseException], ...], func: Callable[[], object], msg: str = '') None[source]

Record a soft assertion failure if func does not raise expected_exception.

behave_kit.assertions.soft.assert_soft_true(condition: object, msg: str = '') None[source]

Record a soft assertion failure if condition is not truthy.

behave_kit.assertions.soft.soft_asserts() Iterator[SoftAssertCollector][source]

Context manager for soft assertions outside of Behave (e.g. unit tests).

Raises an AssertionError summarizing every failure when the block exits without an exception of its own.

behave_kit.assertions.soft.use_soft_asserts(context: Context) SoftAssertCollector[source]

Activate soft assertions for the given Behave context.

Attaches a fresh SoftAssertCollector to context._behave_kit_soft and makes it the active collector for assert_soft and friends.

Formatting of collected soft-assertion failures into legible output.

class behave_kit.assertions.reporter.SoftAssertReport(failures: list[SoftFailure] = <factory>)[source]

Bases: object

Aggregated report of every soft assertion failure in a scenario.

property failure_count: int

Number of recorded soft assertion failures.

failures: list[SoftFailure]
property has_failures: bool

Whether any soft assertion failure has been recorded.

class behave_kit.assertions.reporter.SoftFailure(message: str, expected: object = <object object>, actual: object = <object object>)[source]

Bases: object

A single soft assertion failure.

actual: object = <object object>
expected: object = <object object>
property has_values: bool

Whether at least one of expected or actual was supplied.

message: str

Assertions that fail with a legible diff instead of a bare AssertionError.

behave_kit.assertions.diff.assert_dict_contains(d: Mapping[Any, Any], subset: Mapping[Any, Any], *, msg: str = '') None[source]

Assert that d contains every key/value pair present in subset.

behave_kit.assertions.diff.assert_json_equals(actual: object, expected: object, *, msg: str = '', options: CompareOptions | None = None) None[source]

Assert that actual deep-equals expected, showing which keys differ.

behave_kit.assertions.diff.assert_list_ordered(lst: Sequence[Any], *, key: Callable[[Any], Any] | None = None, msg: str = '') None[source]

Assert that lst is sorted, optionally by a key function.

behave_kit.assertions.diff.assert_table_equals(actual: _TableLike, expected: _TableLike, *, msg: str = '') None[source]

Assert that two Behave Data Tables have the same headings and rows.

Pure, side-effect-free deep comparison.

Every function here takes plain values in and returns a DiffResult out. No Behave dependency, no context, no I/O — fully unit-testable.

class behave_kit.assertions._matchers.CompareOptions(ignore_keys: frozenset[str] = frozenset({}), float_tolerance: float = 1e-09, ignore_order: bool = False, datetime_tolerance: ~datetime.timedelta | None = None, custom_matchers: dict[type, ~collections.abc.Callable[[~typing.Any, ~typing.Any], bool]] = <factory>)[source]

Bases: object

Tunable behavior for deep_compare.

custom_matchers: dict[type, Callable[[Any, Any], bool]]
datetime_tolerance: timedelta | None = None
float_tolerance: float = 1e-09
ignore_keys: frozenset[str] = frozenset({})
ignore_order: bool = False
class behave_kit.assertions._matchers.Diff(path: str, expected: object, actual: object, message: str)[source]

Bases: object

A single difference found while comparing actual to expected.

actual: object
expected: object
message: str
path: str
class behave_kit.assertions._matchers.DiffResult(equal: bool, diffs: list[Diff] = <factory>)[source]

Bases: object

Outcome of a deep_compare call.

diffs: list[Diff]
equal: bool
behave_kit.assertions._matchers.deep_compare(actual: object, expected: object, options: CompareOptions | None = None) DiffResult[source]

Recursively compare actual to expected and report all differences.

Context

Context utilities: typed access, failure dumps, scoped cleanup, and sub-steps.

class behave_kit.context.TypedContext(context: Context, schema: type[T])[source]

Bases: Generic[T]

Schema-validated proxy over a Behave Context.

setup(**kwargs: Any) None[source]

Type-checked assignment of attributes to the real context.

behave_kit.context.cleanup_scoped(context: Context, scope: Scope = Scope.SCENARIO) None[source]

Delete every attribute tracked with @scoped at the given scope.

behave_kit.context.dump_context(context: Context, path: str | Path = 'debug/') Path[source]

Write every JSON-serializable attribute of context to a file.

Non-serializable attributes are skipped with a logging.warning.

Returns:

The path of the written file.

behave_kit.context.dump_context_on_failure(context: Context, scenario: object, path: str | Path = 'debug/') Path | None[source]

Call dump_context only when scenario.status is "failed".

behave_kit.context.run_steps(context: Context, steps: str) None[source]

Execute Gherkin sub-steps with state isolation.

Wraps context.execute_steps() with:

  • Scenario Outline <placeholder> substitution from context.active_outline.

  • Guaranteed restoration of context.table and context.text.

Parameters:
  • context – The Behave context for the current scenario.

  • steps – Gherkin step text to execute. Must be a string containing one or more step definitions with keywords (Given/When/Then/And/But).

Raises:
behave_kit.context.scoped(name: str, scope: Scope = Scope.SCENARIO) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Decorator: track context.<name> so it is cleaned up automatically.

After the wrapped step function runs, name is registered for cleanup at the given scope (see cleanup_scoped).

Typed proxy over the Behave context.

TypedContext does not subclass or replace behave.runner.Context — it wraps it. Attribute access and assignment are validated against a plain schema class’s annotations, giving IDE autocompletion and mypy support without requiring any migration away from the real context.

class behave_kit.context.typed.TypedContext(context: Context, schema: type[T])[source]

Bases: Generic[T]

Schema-validated proxy over a Behave Context.

setup(**kwargs: Any) None[source]

Type-checked assignment of attributes to the real context.

Automatic cleanup of context attributes registered via @scoped.

Prevents leaks between scenarios: an attribute set with @scoped(“driver”) is deleted from the context once its scope ends, instead of silently surviving into the next scenario.

behave_kit.context.scoped.cleanup_scoped(context: Context, scope: Scope = Scope.SCENARIO) None[source]

Delete every attribute tracked with @scoped at the given scope.

behave_kit.context.scoped.scoped(name: str, scope: Scope = Scope.SCENARIO) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Decorator: track context.<name> so it is cleaned up automatically.

After the wrapped step function runs, name is registered for cleanup at the given scope (see cleanup_scoped).

Dump the Behave context to JSON for post-mortem debugging.

behave.runner.Context stores user attributes in an internal stack of dict “layers” (context._stack), not in the instance __dict__. This module reads that stack when available and falls back to vars(context) for simple mock contexts (e.g. types.SimpleNamespace) used in tests.

behave_kit.context.dump.dump_context(context: Context, path: str | Path = 'debug/') Path[source]

Write every JSON-serializable attribute of context to a file.

Non-serializable attributes are skipped with a logging.warning.

Returns:

The path of the written file.

behave_kit.context.dump.dump_context_on_failure(context: Context, scenario: object, path: str | Path = 'debug/') Path | None[source]

Call dump_context only when scenario.status is "failed".

Execute Gherkin sub-steps with state isolation and outline substitution.

run_steps wraps Behave’s context.execute_steps() with two enhancements that make sub-step execution safer and more expressive:

  1. Scenario Outline substitution<placeholder> variables from the current outline row are replaced before parsing, so sub-steps can reference the same parameters as the parent step.

  2. Table / text preservationcontext.table and context.text are saved before execution and restored in a finally block, so the parent step’s tabular or multiline data is never clobbered by a sub-step.

Usage:

from behave_kit import run_steps

@when("I complete the checkout flow")
def step_impl(context):
    run_steps(context, '''
        Given I have items in my cart
        When I enter shipping info for "<city>"
        And I select payment method "<method>"
        Then I should see the order confirmation
    ''')
behave_kit.context.substeps.run_steps(context: Context, steps: str) None[source]

Execute Gherkin sub-steps with state isolation.

Wraps context.execute_steps() with:

  • Scenario Outline <placeholder> substitution from context.active_outline.

  • Guaranteed restoration of context.table and context.text.

Parameters:
  • context – The Behave context for the current scenario.

  • steps – Gherkin step text to execute. Must be a string containing one or more step definitions with keywords (Given/When/Then/And/But).

Raises:

Continue after failed

Continue executing remaining steps after a step fails.

By default, Behave stops a scenario at the first failing step. This module provides utilities to change that behaviour globally or temporarily.

Usage in environment.py:

from behave_kit import continue_after_failed

def before_all(context):
    continue_after_failed(True)

Or as a context manager for temporary activation:

from behave_kit import continue_on_failure

def before_scenario(context, scenario):
    if "smoke" in scenario.tags:
        context._caf_cm = continue_on_failure()
        context._caf_cm.__enter__()

def after_scenario(context, scenario):
    cm = getattr(context, "_caf_cm", None)
    if cm is not None:
        cm.__exit__(None, None, None)
behave_kit.continue_after_failed.continue_after_failed(enabled: bool = True) None[source]

Set whether scenarios continue executing after a failed step.

Sets the class-level attribute Scenario.continue_after_failed_step which Behave checks before aborting a scenario on step failure.

Parameters:

enabled – When True, scenarios keep running remaining steps even after a step fails. When False, execution stops at the first failing step (Behave’s default).

Raises:

BehaveKitError – If enabled is not a boolean.

behave_kit.continue_after_failed.continue_on_failure() Iterator[None][source]

Temporarily enable continue-after-failed for the current block.

Sets Scenario.continue_after_failed_step to True on entry and restores the previous value on exit, even if an exception occurs.

Usage:

with continue_on_failure():
    # any scenario that runs while inside this block
    # will continue after failed steps
    ...

Skip

Conditional skip decorators for Behave step functions.

behave_kit.skip.skip_if_env(env_name: str) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Skip the step when context.config.env == env_name.

behave_kit.skip.skip_if_missing(module_name: str) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Skip the step when module_name cannot be imported.

behave_kit.skip.skip_if_no_browser(func: Callable[[Concatenate[Context, P]], R] | None = None) Callable[[Concatenate[Context, P]], R] | Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Skip the step when Selenium is not installed.

Can be applied directly (@skip_if_no_browser) or called as a decorator factory (@skip_if_no_browser()).

behave_kit.skip.skip_on_os(os_name: str) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Skip the step when running on os_name.

Conditional skip decorators for Behave step functions.

Behave has no step.skip() API. The idiomatic way to skip a step (and have Behave report it as skipped, not failed) is to raise unittest.SkipTest from within the step. Every decorator here checks its condition at call time, not at decoration time, so context (and context.config) can be inspected.

behave_kit.skip.decorators.skip_if_env(env_name: str) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Skip the step when context.config.env == env_name.

behave_kit.skip.decorators.skip_if_missing(module_name: str) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Skip the step when module_name cannot be imported.

behave_kit.skip.decorators.skip_if_no_browser(func: Callable[[Concatenate[Context, P]], R] | None = None) Callable[[Concatenate[Context, P]], R] | Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Skip the step when Selenium is not installed.

Can be applied directly (@skip_if_no_browser) or called as a decorator factory (@skip_if_no_browser()).

behave_kit.skip.decorators.skip_on_os(os_name: str) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Skip the step when running on os_name.

Reusable skip conditions, usable standalone or via skip.decorators.

behave_kit.skip.conditions.is_env(context: Context, env_name: str) bool[source]

True if context.config.env == env_name.

behave_kit.skip.conditions.is_missing(module_name: str) bool[source]

True if module_name cannot be imported.

Invalid inputs (non-string or empty) are treated as missing to avoid executing arbitrary code.

behave_kit.skip.conditions.is_no_browser() bool[source]

True if Selenium is not installed.

behave_kit.skip.conditions.is_os(os_name: str) bool[source]

True if the current OS matches os_name (case-insensitive).

Environment

Environment management: behave.toml profiles and typed env() reads.

class behave_kit.env.KitConfig(env: str, base_url: str = '', browser: str = '', timeouts: Mapping[str, int]=<factory>, credentials: Mapping[str, str]=<factory>, raw: Mapping[str, ~typing.Any]=<factory>)[source]

Bases: object

Read-only, deeply immutable resolved configuration.

base_url: str = ''
browser: str = ''
credentials: Mapping[str, str]
env: str
classmethod from_toml(path: str | Path, *, env: str | None = None, overrides: dict[str, str] | None = None) KitConfig[source]

Load a KitConfig from a TOML file.

env defaults to behave_kit.env if omitted. CLI --set overrides are dotted keys flattened to top-level strings.

Raises:

ConfigError – if the file is missing, unreadable, malformed, or the requested profile does not exist.

get(key: str, default: Any = None) Any[source]

Return the raw configuration value for key or default.

raw: Mapping[str, Any]
timeouts: Mapping[str, int]
behave_kit.env.env(key: str, *, required: Literal[True] = True, var_type: type[T] = <class 'str'>, default: Any = None, context: Context | None = None) T[source]
behave_kit.env.env(key: str, *, required: ~typing.Literal[False], var_type: type[~behave_kit.env.variables.T] = <class 'str'>, default: ~typing.Any = None, context: ~behave.runner.Context | None = None) T | None

Read environment variable key, converted to var_type.

Resolution order: os.environ -> context.config.raw (if context is given) -> default -> EnvVarError (if required).

behave_kit.env.env_snapshot() Iterator[None][source]

Snapshot os.environ and restore it on exit.

Any additions, modifications, or deletions made to environment variables inside the block are reverted when the block exits — even if an exception is raised.

behave_kit.env.load_env_config(context: Context, env: str, config_file: str | Path = 'behave.toml', *, overrides: dict[str, str] | None = None) KitConfig[source]

Load config_file, resolve the env profile, and attach it to context.config.

env() — read environment variables with validation and type conversion.

Falls back to the KitConfig attached to context.config (see behave_kit.env.config.load_env_config) when the variable is not present in os.environ. Loads a .env file once via python-dotenv if installed.

behave_kit.env.variables.env(key: str, *, required: Literal[True] = True, var_type: type[T] = <class 'str'>, default: Any = None, context: Context | None = None) T[source]
behave_kit.env.variables.env(key: str, *, required: ~typing.Literal[False], var_type: type[~behave_kit.env.variables.T] = <class 'str'>, default: ~typing.Any = None, context: ~behave.runner.Context | None = None) T | None

Read environment variable key, converted to var_type.

Resolution order: os.environ -> context.config.raw (if context is given) -> default -> EnvVarError (if required).

Wiring: load behave.toml and attach the resolved config to the Behave context.

behave_kit.env.config.load_env_config(context: Context, env: str, config_file: str | Path = 'behave.toml', *, overrides: dict[str, str] | None = None) KitConfig[source]

Load config_file, resolve the env profile, and attach it to context.config.

Profile selection and override application for raw behave.toml data.

Operates on plain dicts (as returned by tomllib.load) so profile resolution can be unit-tested without touching the filesystem or KitConfig.

behave_kit.env.profiles.apply_overrides(config_dict: dict[str, Any], overrides: dict[str, str]) dict[str, Any][source]

Return config_dict with CLI --set key=value overrides applied on top.

behave_kit.env.profiles.select_profile(toml_data: dict[str, Any], env_name: str) dict[str, Any][source]

Return the [env.<env_name>] section merged over [env.default].

Raises:

ConfigError – if env_name has no matching section or the TOML structure is invalid.

env_snapshot — save and restore environment variables.

Context manager that snapshots os.environ on entry and restores it on exit, so tests that set environment variables do not leak state.

behave_kit.env.snapshot.env_snapshot() Iterator[None][source]

Snapshot os.environ and restore it on exit.

Any additions, modifications, or deletions made to environment variables inside the block are reverted when the block exits — even if an exception is raised.

Data

Test data loading: CSV/JSON/YAML/XLSX with caching and named providers.

class behave_kit.data.DataCache[source]

Bases: object

Cache of loaded data, keyed by (path, scope).

get(path: str | Path, scope: Scope = Scope.SCENARIO) object | None[source]

Return the cached data for path at scope, or None.

Parameters:
  • path – File path or identifier used as the cache key.

  • scope – Scope whose cached value should be returned.

invalidate(scope: Scope) None[source]

Remove every entry cached at scope.

set(path: str | Path, scope: Scope, data: object) None[source]

Store data for path under scope.

Parameters:
  • path – File path or identifier used as the cache key.

  • scope – Scope at which the data should be cached.

  • data – Value to cache.

behave_kit.data.data_provider(name: str) Callable[[Callable[[...], object]], Callable[[...], object]][source]

Register func as the data provider named name.

behave_kit.data.get_provider(name: str) Callable[[...], object][source]

Return the provider function registered under name.

behave_kit.data.load_data(path: str | Path) dict[str, Any] | list[Any][source]

Load path and return its parsed content, dispatched by extension.

Raises:

DataLoadError – if the file is missing, the extension is unsupported, or a required optional dependency (pyyaml, openpyxl) is not installed.

behave_kit.data.load_examples_from(path: str | Path) list[dict[str, Any]][source]

Load path and normalize the result to a list of dicts.

Multi-format test data loading: CSV, JSON, YAML, XLSX.

Each format loader raises DataLoadError (never a bare parser or file-system exception) with a suggestion naming the missing optional dependency or the invalid path.

behave_kit.data.loader.load_data(path: str | Path) dict[str, Any] | list[Any][source]

Load path and return its parsed content, dispatched by extension.

Raises:

DataLoadError – if the file is missing, the extension is unsupported, or a required optional dependency (pyyaml, openpyxl) is not installed.

behave_kit.data.loader.load_examples_from(path: str | Path) list[dict[str, Any]][source]

Load path and normalize the result to a list of dicts.

Per-scope cache for loaded test data.

Avoids re-reading the same file multiple times within a scenario (or feature, if requested). Keys are (path, scope) pairs.

class behave_kit.data.cache.DataCache[source]

Bases: object

Cache of loaded data, keyed by (path, scope).

get(path: str | Path, scope: Scope = Scope.SCENARIO) object | None[source]

Return the cached data for path at scope, or None.

Parameters:
  • path – File path or identifier used as the cache key.

  • scope – Scope whose cached value should be returned.

invalidate(scope: Scope) None[source]

Remove every entry cached at scope.

set(path: str | Path, scope: Scope, data: object) None[source]

Store data for path under scope.

Parameters:
  • path – File path or identifier used as the cache key.

  • scope – Scope at which the data should be cached.

  • data – Value to cache.

@data_provider — register named functions that produce test data on demand.

Uses the shared Registry backbone (see behave_kit._core.registry), so registration/lookup errors surface as the same FixtureError used by fixtures and parameter types.

behave_kit.data.providers.data_provider(name: str) Callable[[Callable[[...], object]], Callable[[...], object]][source]

Register func as the data provider named name.

behave_kit.data.providers.get_provider(name: str) Callable[[...], object][source]

Return the provider function registered under name.

Fixtures

Tag-based fixtures with lifecycle and dependency resolution.

class behave_kit.fixtures.FixtureManager[source]

Bases: object

Orchestrates fixture setup/teardown lifecycle by scope.

setup_for_feature(context: Context, feature: object) None[source]

Run feature-scoped fixtures matching feature tags.

setup_for_scenario(context: Context, scenario: object) None[source]

Run scenario-scoped fixtures matching scenario tags.

teardown_feature(context: Context) None[source]

Run feature-scoped teardowns in reverse order.

teardown_scenario(context: Context) None[source]

Run scenario-scoped teardowns in reverse order.

behave_kit.fixtures.fixture(name: str, scope: Scope = Scope.SCENARIO, requires: str | list[str] | None = None) Callable[[Callable[[Context], tuple[Callable[[Context], None], Callable[[Context], None]] | None]], Callable[[Context], tuple[Callable[[Context], None], Callable[[Context], None]] | None]][source]

Register func as the fixture named name.

Parameters:
  • name – Fixture name (matches a Behave tag).

  • scope – Lifetime of the fixture (scenario or feature).

  • requires – Other fixture names this one depends on. Accepts a single string or a list of strings.

@fixture — register tag-based fixtures with scope and dependencies.

A fixture is a callable (context) -> FixtureResult where FixtureResult is either:

  • None — the function performed setup directly, no teardown needed.

  • (setup_fn, teardown_fn) — two callables, both receiving context. setup_fn is called immediately; teardown_fn is deferred until the scope ends.

Fixture names match Behave tags: a scenario tagged @database triggers the fixture named "database".

behave_kit.fixtures.registry.fixture(name: str, scope: Scope = Scope.SCENARIO, requires: str | list[str] | None = None) Callable[[Callable[[Context], tuple[Callable[[Context], None], Callable[[Context], None]] | None]], Callable[[Context], tuple[Callable[[Context], None], Callable[[Context], None]] | None]][source]

Register func as the fixture named name.

Parameters:
  • name – Fixture name (matches a Behave tag).

  • scope – Lifetime of the fixture (scenario or feature).

  • requires – Other fixture names this one depends on. Accepts a single string or a list of strings.

behave_kit.fixtures.registry.fixture_names(scope: Scope | None = None) list[str][source]

Return registered fixture names, optionally filtered by scope.

behave_kit.fixtures.registry.fixture_scope(name: str) Scope[source]

Return the scope of the fixture registered under name.

behave_kit.fixtures.registry.get_fixture(name: str) Callable[[Context], tuple[Callable[[Context], None], Callable[[Context], None]] | None][source]

Return the fixture factory registered under name.

behave_kit.fixtures.registry.resolve_fixture_order(name: str) list[str][source]

Return the dependency chain for name in setup order.

FixtureManager — orchestrates setup/teardown by tags and scope.

Usage in environment.py:

from behave_kit.fixtures import FixtureManager

fixtures = FixtureManager()

def before_scenario(context, scenario):
    fixtures.setup_for_scenario(context, scenario)

def after_scenario(context):
    fixtures.teardown_scenario(context)
class behave_kit.fixtures.manager.FixtureManager[source]

Bases: object

Orchestrates fixture setup/teardown lifecycle by scope.

setup_for_feature(context: Context, feature: object) None[source]

Run feature-scoped fixtures matching feature tags.

setup_for_scenario(context: Context, scenario: object) None[source]

Run scenario-scoped fixtures matching scenario tags.

teardown_feature(context: Context) None[source]

Run feature-scoped teardowns in reverse order.

teardown_scenario(context: Context) None[source]

Run scenario-scoped teardowns in reverse order.

Steps

Parameter types, conditional steps, class-based steps, and suggestions.

behave_kit.steps.convert(name: str, match_string: str) Any[source]

Apply the converter registered under name to match_string.

behave_kit.steps.data_driven(path: str | Path) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Decorator: run the step once per row in the data file at path.

Each row (a dict) is unpacked as keyword arguments into the step function. Column names with hyphens or spaces are converted to valid Python identifiers (- and `` `` → _).

Usage:

@data_driven("tests/data/users.csv")
@when("I login as {username}")
def step(context, username=None, password=None): ...
Raises:

BehaveKitError – If the data file cannot be loaded or a row is not a dict.

behave_kit.steps.parameter_type(name: str, pattern: str = '') Callable[[Callable[[str], Any]], Callable[[str], Any]][source]

Register func as the converter for parameter type name.

pattern documents the expected input shape; matching the pattern against step text is left to the caller (Cucumber Expressions, parse, or a manual regex) – this decorator only registers the conversion.

behave_kit.steps.register_builtin_types() None[source]

Register every built-in converter. Safe to call more than once.

behave_kit.steps.setup_suggestions(context: Context) Callable[[Context, object], None][source]

Return an after_step(context, step) hook that logs suggestions.

Wire it in environment.py:

_suggest = behave_kit.steps.setup_suggestions(context)

def after_step(context, step):
    _suggest(context, step)
behave_kit.steps.step_impl_base(default_matcher: Any = None) type[source]

Return a new base class for class-based step implementations.

Each call creates an isolated local step registry, so independent step libraries can coexist without interfering with each other. Subclasses share the registry of the base they derive from, which is what enables extension via subclassing and method overriding.

Parameters:

default_matcher – Default matcher for steps that don’t specify one. Accepts None (Behave’s current default), a string name ("parse", "re", "cfparse", "simplified", "cucumber"), or a Matcher subclass.

Returns:

A base class with given, when, then, step decorator factories and register() / clear() methods.

Raises:

StepError – If default_matcher is not a valid matcher reference.

behave_kit.steps.suggest_for_undefined(step: object) list[str][source]

Log the closest registered step pattern(s) for an undefined step.

Returns the list of close matches found (possibly empty).

behave_kit.steps.teardown_steps(context: Context) None[source]

Call teardown() on every live step-instance for context and clear them.

Safe to call when no class-based steps have run (no-op). Wired automatically by behave_kit.setup() / behave_kit.teardown().

Iterates over a snapshot of the instances dict so that a teardown() method that triggers additional step execution (which could add new instances) does not cause RuntimeError: dictionary changed size during iteration.

behave_kit.steps.when_if(condition: Callable[[Context], object]) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Decorator: execute the step only if condition(context) is true.

When the condition is false, the step is skipped (via unittest.SkipTest) rather than executed or failed.

@when_if — run a step only when a condition holds, skip silently otherwise.

behave_kit.steps.conditional.when_if(condition: Callable[[Context], object]) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Decorator: execute the step only if condition(context) is true.

When the condition is false, the step is skipped (via unittest.SkipTest) rather than executed or failed.

Better error messages: suggest similar steps for undefined step names.

Does not wrap or modify Behave’s step registry. Reads step patterns from behave.step_registry.registry (a public module-level singleton) and compares them against the undefined step’s text with difflib.

behave_kit.steps.suggestions.setup_suggestions(context: Context) Callable[[Context, object], None][source]

Return an after_step(context, step) hook that logs suggestions.

Wire it in environment.py:

_suggest = behave_kit.steps.setup_suggestions(context)

def after_step(context, step):
    _suggest(context, step)
behave_kit.steps.suggestions.suggest_for_undefined(step: object) list[str][source]

Log the closest registered step pattern(s) for an undefined step.

Returns the list of close matches found (possibly empty).

@parameter_type — register custom step-parameter converters.

Ships built-in converters for common types (int, float, date, datetime, decimal, email, url) via register_builtin_types().

behave_kit.steps.parameters.convert(name: str, match_string: str) Any[source]

Apply the converter registered under name to match_string.

behave_kit.steps.parameters.get_parameter_type_pattern(name: str) str | None[source]

Return the registered pattern for name, or None if not registered.

behave_kit.steps.parameters.parameter_type(name: str, pattern: str = '') Callable[[Callable[[str], Any]], Callable[[str], Any]][source]

Register func as the converter for parameter type name.

pattern documents the expected input shape; matching the pattern against step text is left to the caller (Cucumber Expressions, parse, or a manual regex) – this decorator only registers the conversion.

behave_kit.steps.parameters.register_builtin_types() None[source]

Register every built-in converter. Safe to call more than once.

@data_driven — run a step once per row of a data file.

Loads test data via behave_kit.data.load_data and executes the decorated step function once for each row, injecting the row’s keys as keyword arguments (with - replaced by _ for valid Python identifiers).

behave_kit.steps.data_driven.data_driven(path: str | Path) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Decorator: run the step once per row in the data file at path.

Each row (a dict) is unpacked as keyword arguments into the step function. Column names with hyphens or spaces are converted to valid Python identifiers (- and `` `` → _).

Usage:

@data_driven("tests/data/users.csv")
@when("I login as {username}")
def step(context, username=None, password=None): ...
Raises:

BehaveKitError – If the data file cannot be loaded or a row is not a dict.

Class-based step implementations with per-scenario instances.

This module provides an alternative to Behave’s function-based step decorators (@given, @when, @then) that lets you organise step implementations as methods on a class. Each scenario gets a fresh instance with the Behave context exposed as self.context, so state can be kept on self instead of polluting context.

Features:

  • Step definitions live on a class as methods — group related steps together.

  • Subclassing and method overriding work as usual Python inheritance.

  • A per-step matcher can be selected without changing global state.

  • self.context is bound automatically — no context parameter needed.

  • setup() / teardown() hooks run once per scenario per class.

  • register() is idempotent; clear() removes previously registered steps.

Usage:

from behave_kit import step_impl_base

Base = step_impl_base()

class AccountSteps(Base):
    @Base.given("I have a balance of {amount:d}")
    def set_balance(self, amount):
        self.balance = amount

    @Base.when("I deposit {amount:d}")
    def deposit(self, amount):
        self.balance += amount

    @Base.then("the balance should be {expected:d}")
    def check_balance(self, expected):
        assert self.balance == expected

    @property
    def balance(self):
        return getattr(self.context, "balance", 0)

    @balance.setter
    def balance(self, value):
        self.context.balance = value

AccountSteps.register()

Wire the teardown hook in environment.py so teardown() methods run:

from behave_kit import setup, teardown

def before_all(context):
    setup(context)

def after_scenario(context, scenario):
    teardown(context)
behave_kit.steps.classes.step_impl_base(default_matcher: Any = None) type[source]

Return a new base class for class-based step implementations.

Each call creates an isolated local step registry, so independent step libraries can coexist without interfering with each other. Subclasses share the registry of the base they derive from, which is what enables extension via subclassing and method overriding.

Parameters:

default_matcher – Default matcher for steps that don’t specify one. Accepts None (Behave’s current default), a string name ("parse", "re", "cfparse", "simplified", "cucumber"), or a Matcher subclass.

Returns:

A base class with given, when, then, step decorator factories and register() / clear() methods.

Raises:

StepError – If default_matcher is not a valid matcher reference.

behave_kit.steps.classes.teardown_steps(context: Context) None[source]

Call teardown() on every live step-instance for context and clear them.

Safe to call when no class-based steps have run (no-op). Wired automatically by behave_kit.setup() / behave_kit.teardown().

Iterates over a snapshot of the instances dict so that a teardown() method that triggers additional step execution (which could add new instances) does not cause RuntimeError: dictionary changed size during iteration.

Hooks

Wiring layer: connect all behave-kit modules to a Behave context.

setup() is idempotent and fault-tolerant — if one module fails to wire, the others still work. teardown() only cleans up what was successfully wired, in reverse order.

Usage in environment.py:

from behave_kit import setup, teardown

def before_all(context):
    setup(context, env="staging")

def after_scenario(context, scenario):
    teardown(context)
behave_kit.hooks.setup(context: Context, *, env: str | None = None, config_file: str = 'behave.toml', log_level: str = 'INFO', continue_after_failed: bool | None = None) None[source]

Wire all behave-kit modules into context.

Idempotent: calling twice is a no-op. Each module is wired independently in try/except — a failure in one does not prevent the others.

Parameters:
  • context – The Behave context object.

  • env – Optional environment name for profile-based configuration.

  • config_file – Path to the configuration file (default behave.toml).

  • log_level – Logging level for the behave_kit logger.

  • continue_after_failed – When True, scenarios continue executing remaining steps after a failure. When False, the default Behave behaviour (stop on first failure) is restored. None leaves the current setting unchanged.

behave_kit.hooks.teardown(context: Context) None[source]

Clean up wired modules in reverse order.

Only modules that were successfully wired during setup() are torn down. Safe to call without a prior setup() (no-op). Also tears down any class-based step instances created during the scenario, even if setup() was not called.

Utilities

wait_until — poll a condition until it becomes true or a timeout is reached.

Common in E2E and integration tests where a resource (API, database, browser) is not immediately available after an action.

behave_kit.wait.wait_until(condition: Callable[[], object], *, timeout: float = 10.0, interval: float = 0.5, message: str = '') None[source]

Poll condition until it is truthy or timeout seconds elapse.

Parameters:
  • condition – Zero-argument callable whose return value is evaluated with scalar-bool coercion (tolerates array-like objects).

  • timeout – Maximum number of seconds to wait (default 10).

  • interval – Seconds between polls (default 0.5).

  • message – Custom message for the timeout error.

Raises:

TimeoutError – If condition does not become truthy within timeout seconds.

get_path — navigate nested dicts with dot notation.

Avoids chaining [] and handling KeyError / TypeError manually when extracting a value from a deeply nested JSON/dict response.

behave_kit.utils.get_path(data: object, path: str, default: Any = <object object>) Any[source]

Return the value at path within nested data, or default.

path uses dot notation to traverse nested dicts:

get_path(response, “user.address.city”)

Numeric segments traverse lists:

get_path(response, “users.0.name”)

Parameters:
  • data – The root dict or list to traverse.

  • path – Dot-separated path string.

  • default – Value to return when the path does not exist. If not provided, BehaveKitError is raised instead.

Raises:

BehaveKitError – When the path is not found and no default is given.

assert_under and @timed — time-based assertions for performance checks.

assert_under verifies that a callable completes within a deadline. @timed is a decorator that asserts a step finishes within a time limit.

exception behave_kit.timing.TimeoutExceededError(message: str, *, cause: BaseException | None = None, suggestion: str | None = None)[source]

Bases: BehaveKitError

Raised when a callable takes longer than the allowed time.

behave_kit.timing.assert_under(seconds: float, func: Callable[[], R], *, message: str = '') R[source]

Run func and assert it completes within seconds.

Parameters:
  • seconds – Maximum allowed execution time.

  • func – Zero-argument callable to time.

  • message – Custom message for the timeout error.

Returns:

The return value of func.

Raises:

TimeoutExceededError – If func takes longer than seconds.

behave_kit.timing.timed(seconds: float) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Decorator: assert a step function completes within seconds.

Usage:

@timed(2.0)
@when("I fetch the data")
def step(context): ...
Raises:

BehaveKitError – If seconds is negative.

temp_workspace — isolated temporary directory for filesystem tests.

Creates a temporary directory, changes the working directory into it, and restores both on exit. Useful for tests that create or read files without polluting the project tree.

behave_kit.workspace.temp_workspace(*, prefix: str = 'behave_kit_') Iterator[Path][source]

Yield a temporary directory and restore the CWD on exit.

Parameters:

prefix – Prefix for the temporary directory name.

Yields:

Path to the temporary directory (the CWD while inside the block).

Core

Exception hierarchy shared across behave-kit.

Every public function that can fail raises a subclass of BehaveKitError with an actionable message: what happened, why (cause), and what to do about it (suggestion).

exception behave_kit._core.errors.BehaveKitError(message: str, *, cause: BaseException | None = None, suggestion: str | None = None)[source]

Bases: Exception

Base exception for all behave-kit errors.

exception behave_kit._core.errors.ConfigError(message: str, *, cause: BaseException | None = None, suggestion: str | None = None)[source]

Bases: BehaveKitError

Raised when behave.toml is invalid or a profile cannot be found.

exception behave_kit._core.errors.DataLoadError(message: str, *, cause: BaseException | None = None, suggestion: str | None = None)[source]

Bases: BehaveKitError

Raised when test data cannot be loaded (missing file, bad format, missing dependency).

exception behave_kit._core.errors.EnvVarError(message: str, *, cause: BaseException | None = None, suggestion: str | None = None)[source]

Bases: BehaveKitError

Raised when a required environment variable is missing or invalid.

exception behave_kit._core.errors.FixtureError(message: str, *, cause: BaseException | None = None, suggestion: str | None = None)[source]

Bases: BehaveKitError

Raised when a fixture is missing or has a circular dependency.

exception behave_kit._core.errors.ScopeError(message: str, *, cause: BaseException | None = None, suggestion: str | None = None)[source]

Bases: BehaveKitError, AttributeError

Raised when a TypedContext attribute is accessed or set without being declared.

exception behave_kit._core.errors.StepError(message: str, *, cause: BaseException | None = None, suggestion: str | None = None)[source]

Bases: BehaveKitError

Raised when a step is ambiguous or a parameter type is invalid.

exception behave_kit._core.errors.SubStepError(message: str, *, cause: BaseException | None = None, suggestion: str | None = None)[source]

Bases: BehaveKitError

Raised when sub-step execution fails or is used outside a feature context.

Shared type aliases and extension Protocols.

Extension points (custom loaders, skip conditions, diff matchers, fixtures) are defined as Protocol classes. Anything that structurally satisfies the Protocol can be registered — no base-class inheritance required.

class behave_kit._core.types.DataLoader(*args, **kwargs)[source]

Bases: Protocol

Loads test data from a file into a Python object.

load(path: Path) object[source]

Load and return the data stored at path.

Parameters:

path – Path to the data file.

Returns:

The loaded data as a Python object.

class behave_kit._core.types.DiffMatcher(*args, **kwargs)[source]

Bases: Protocol

Compares two values of a specific type and reports differences.

matches(actual: object, expected: object) object[source]

Compare actual against expected and return a diff result.

Parameters:
  • actual – Value produced by the code under test.

  • expected – Expected value.

Returns:

An object describing the comparison (e.g. True if equal, or a structured diff otherwise).

class behave_kit._core.types.FixtureFn(*args, **kwargs)[source]

Bases: Protocol

Sets up a resource for a scenario/feature and tears it down afterwards.

teardown(context: Context, resource: object) None[source]

Clean up the resource produced by this fixture.

Parameters:
  • context – Behave context for the current scenario.

  • resource – Object returned by __call__.

class behave_kit._core.types.Scope(*values)[source]

Bases: Enum

Lifetime of a registered item (fixture, scoped attribute, cached data).

FEATURE = 'feature'
GLOBAL = 'global'
SCENARIO = 'scenario'
class behave_kit._core.types.SkipCondition(*args, **kwargs)[source]

Bases: Protocol

Decides whether a step or scenario should be skipped.

should_skip(context: Context) bool[source]

Return True if the step/scenario should be skipped.

Parameters:

context – Behave context for the current scenario.

Returns:

Whether the condition to skip is met.

Project-wide configuration object loaded from behave.toml.

KitConfig exposes the subset of fields behave-kit cares about, while raw preserves the full merged profile for custom tooling.

class behave_kit._core.config.KitConfig(env: str, base_url: str = '', browser: str = '', timeouts: Mapping[str, int]=<factory>, credentials: Mapping[str, str]=<factory>, raw: Mapping[str, ~typing.Any]=<factory>)[source]

Bases: object

Read-only, deeply immutable resolved configuration.

base_url: str = ''
browser: str = ''
credentials: Mapping[str, str]
env: str
classmethod from_toml(path: str | Path, *, env: str | None = None, overrides: dict[str, str] | None = None) KitConfig[source]

Load a KitConfig from a TOML file.

env defaults to behave_kit.env if omitted. CLI --set overrides are dotted keys flattened to top-level strings.

Raises:

ConfigError – if the file is missing, unreadable, malformed, or the requested profile does not exist.

get(key: str, default: Any = None) Any[source]

Return the raw configuration value for key or default.

raw: Mapping[str, Any]
timeouts: Mapping[str, int]

Generic registry with dependency resolution.

Backbone for fixtures, parameter types and data providers. A single Registry[T] handles registration, lookup, scope filtering and dependency resolution (including circular-dependency detection) for all three.

class behave_kit._core.registry.Registry[source]

Bases: Generic[T]

Named registry of items with scopes and dependencies.

get(name: str) T[source]

Return the item registered under name.

Raises:

FixtureError – if name is not registered.

names(scope: Scope | None = None) list[str][source]

Return registered names, optionally filtered by scope.

register(name: str, item: T, *, scope: Scope = Scope.SCENARIO, requires: list[str] | None = None) None[source]

Register item under name.

Raises:

FixtureError – if name is already registered.

resolve_dependencies(name: str) list[str][source]

Return the dependency chain for name in setup order.

The returned list contains name’s dependencies followed by name itself, each dependency appearing before anything that requires it.

Raises:

FixtureError – if name (or a transitive dependency) is not registered, or if a circular dependency is detected.

scope_of(name: str) Scope[source]

Return the scope of the item registered under name.

Per-module logger helper.

Every behave-kit module gets a logger named behave_kit.<module>, so the log level can be configured globally via logging.getLogger(“behave_kit”).

behave_kit._core.logging.get_logger(name: str) Logger[source]

Return the logger for a behave-kit submodule.

Parameters:

name – Short module name, e.g. "assertions" or "fixtures".

Returns:

A logger named behave_kit.<name>.

Runtime compatibility detection: interpreter/behave versions and optional deps.

Never installs anything automatically — only exposes boolean flags so callers can raise an actionable error naming the missing extra.