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:
Automatic wiring —
behave_kit.setup(context)inbefore_all.Cherry-pick —
from behave_kit.assertions import assert_soft.Namespace —
import 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:
objectTunable behavior for deep_compare.
- class behave_kit.DataCache[source]¶
Bases:
objectCache of loaded data, keyed by
(path, scope).
- 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:
objectRead-only, deeply immutable resolved configuration.
- classmethod from_toml(path: str | Path, *, env: str | None = None, overrides: dict[str, str] | None = None) KitConfig[source]¶
Load a
KitConfigfrom a TOML file.envdefaults tobehave_kit.envif omitted. CLI--setoverrides are dotted keys flattened to top-level strings.- Raises:
ConfigError – if the file is missing, unreadable, malformed, or the requested profile does not exist.
- class behave_kit.Scope(*values)[source]¶
Bases:
EnumLifetime 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:
BehaveKitErrorRaised 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.
- behave_kit.assert_dict_contains(d: Mapping[Any, Any], subset: Mapping[Any, Any], *, msg: str = '') None[source]¶
Assert that
dcontains every key/value pair present insubset.
- behave_kit.assert_json_equals(actual: object, expected: object, *, msg: str = '', options: CompareOptions | None = None) None[source]¶
Assert that
actualdeep-equalsexpected, showing which keys differ.
- behave_kit.assert_list_ordered(lst: Sequence[Any], *, key: Callable[[Any], Any] | None = None, msg: str = '') None[source]¶
Assert that
lstis sorted, optionally by akeyfunction.
- behave_kit.assert_soft(condition: bool, msg: str = '') None[source]¶
Record a soft assertion failure if
conditionis 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
valueis 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
funcdoes not raiseexpected_exception.
- behave_kit.assert_soft_true(condition: object, msg: str = '') None[source]¶
Record a soft assertion failure if
conditionis 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
funcand assert it completes withinseconds.- 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
functakes longer thanseconds.
- 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_stepwhich Behave checks before aborting a scenario on step failure.- Parameters:
enabled – When
True, scenarios keep running remaining steps even after a step fails. WhenFalse, execution stops at the first failing step (Behave’s default).- Raises:
BehaveKitError – If
enabledis 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_steptoTrueon 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
nametomatch_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
funcas the data provider namedname.
- behave_kit.deep_compare(actual: object, expected: object, options: CompareOptions | None = None) DiffResult[source]¶
Recursively compare
actualtoexpectedand report all differences.
- behave_kit.dump_context(context: Context, path: str | Path = 'debug/') Path[source]¶
Write every JSON-serializable attribute of
contextto 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.statusis"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 tovar_type.Resolution order:
os.environ->context.config.raw(ifcontextis given) ->default-> EnvVarError (ifrequired).
- behave_kit.env_snapshot() Iterator[None][source]¶
Snapshot
os.environand 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
funcas the fixture namedname.- 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
pathwithin nesteddata, ordefault.pathuses 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
defaultis 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
pathand 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
pathand 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
funcas the converter for parameter typename.patterndocuments 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 fromcontext.active_outline.Guaranteed restoration of
context.tableandcontext.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:
SubStepError – If called outside a feature context.
AssertionError – If any sub-step fails (propagated from Behave).
- 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,
nameis registered for cleanup at the givenscope(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_kitlogger.continue_after_failed – When
True, scenarios continue executing remaining steps after a failure. WhenFalse, the default Behave behaviour (stop on first failure) is restored.Noneleaves 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_namecannot 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,stepdecorator factories andregister()/clear()methods.- Raises:
StepError – If
default_matcheris 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 priorsetup()(no-op). Also tears down any class-based step instances created during the scenario, even ifsetup()was not called.
- behave_kit.teardown_steps(context: Context) None[source]¶
Call
teardown()on every live step-instance forcontextand 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 causeRuntimeError: 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:
Pathto 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
secondsis 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_softand 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
conditionuntil it is truthy ortimeoutseconds 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
conditiondoes not become truthy withintimeoutseconds.
- 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:
objectTunable behavior for deep_compare.
- class behave_kit.assertions.Diff(path: str, expected: object, actual: object, message: str)[source]¶
Bases:
objectA single difference found while comparing
actualtoexpected.
- class behave_kit.assertions.DiffResult(equal: bool, diffs: list[Diff] = <factory>)[source]¶
Bases:
objectOutcome of a deep_compare call.
- class behave_kit.assertions.SoftAssertCollector[source]¶
Bases:
objectAccumulates assertion failures without raising immediately.
- assert_soft(condition: bool, msg: str = '') None[source]¶
Record a failure if
conditionis 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
actualis not equal toexpected.- Parameters:
actual – The value produced by the code under test.
expected – The value
actualis expected to match.msg – Optional override message for the failure.
- assert_soft_is_none(value: object, msg: str = '') None[source]¶
Record a failure if
valueis notNone.- 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
funcdoes not raiseexpected_exception.- Parameters:
expected_exception – Exception type (or tuple of types) that
funcis 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
conditionis not truthy.- Parameters:
condition – Value evaluated with scalar-bool coercion.
msg – Optional override message for the failure.
- property failures: list[SoftFailure]¶
Copy of the recorded soft assertion failures.
- report() SoftAssertReport[source]¶
Return an immutable report of all recorded failures.
- class behave_kit.assertions.SoftAssertReport(failures: list[SoftFailure] = <factory>)[source]¶
Bases:
objectAggregated report of every soft assertion failure in a scenario.
- failures: list[SoftFailure]¶
- class behave_kit.assertions.SoftFailure(message: str, expected: object = <object object>, actual: object = <object object>)[source]¶
Bases:
objectA single soft assertion failure.
- behave_kit.assertions.assert_dict_contains(d: Mapping[Any, Any], subset: Mapping[Any, Any], *, msg: str = '') None[source]¶
Assert that
dcontains every key/value pair present insubset.
- behave_kit.assertions.assert_json_equals(actual: object, expected: object, *, msg: str = '', options: CompareOptions | None = None) None[source]¶
Assert that
actualdeep-equalsexpected, 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
lstis sorted, optionally by akeyfunction.
- behave_kit.assertions.assert_soft(condition: bool, msg: str = '') None[source]¶
Record a soft assertion failure if
conditionis 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
valueis 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
funcdoes not raiseexpected_exception.
- behave_kit.assertions.assert_soft_true(condition: object, msg: str = '') None[source]¶
Record a soft assertion failure if
conditionis 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
actualtoexpectedand 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_softand 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:
objectAccumulates assertion failures without raising immediately.
- assert_soft(condition: bool, msg: str = '') None[source]¶
Record a failure if
conditionis 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
actualis not equal toexpected.- Parameters:
actual – The value produced by the code under test.
expected – The value
actualis expected to match.msg – Optional override message for the failure.
- assert_soft_is_none(value: object, msg: str = '') None[source]¶
Record a failure if
valueis notNone.- 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
funcdoes not raiseexpected_exception.- Parameters:
expected_exception – Exception type (or tuple of types) that
funcis 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
conditionis not truthy.- Parameters:
condition – Value evaluated with scalar-bool coercion.
msg – Optional override message for the failure.
- property failures: list[SoftFailure]¶
Copy of the recorded soft assertion failures.
- 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
conditionis 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
valueis 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
funcdoes not raiseexpected_exception.
- behave_kit.assertions.soft.assert_soft_true(condition: object, msg: str = '') None[source]¶
Record a soft assertion failure if
conditionis 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_softand 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:
objectAggregated report of every soft assertion failure in a scenario.
- failures: list[SoftFailure]¶
- class behave_kit.assertions.reporter.SoftFailure(message: str, expected: object = <object object>, actual: object = <object object>)[source]¶
Bases:
objectA single soft assertion failure.
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
dcontains every key/value pair present insubset.
- behave_kit.assertions.diff.assert_json_equals(actual: object, expected: object, *, msg: str = '', options: CompareOptions | None = None) None[source]¶
Assert that
actualdeep-equalsexpected, 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
lstis sorted, optionally by akeyfunction.
- 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:
objectTunable behavior for deep_compare.
- class behave_kit.assertions._matchers.Diff(path: str, expected: object, actual: object, message: str)[source]¶
Bases:
objectA single difference found while comparing
actualtoexpected.
- class behave_kit.assertions._matchers.DiffResult(equal: bool, diffs: list[Diff] = <factory>)[source]¶
Bases:
objectOutcome of a deep_compare call.
- behave_kit.assertions._matchers.deep_compare(actual: object, expected: object, options: CompareOptions | None = None) DiffResult[source]¶
Recursively compare
actualtoexpectedand 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.
- 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
contextto 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.statusis"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 fromcontext.active_outline.Guaranteed restoration of
context.tableandcontext.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:
SubStepError – If called outside a feature context.
AssertionError – If any sub-step fails (propagated from Behave).
- 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,
nameis registered for cleanup at the givenscope(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.
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,
nameis registered for cleanup at the givenscope(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
contextto 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.statusis"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:
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.Table / text preservation —
context.tableandcontext.textare saved before execution and restored in afinallyblock, 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 fromcontext.active_outline.Guaranteed restoration of
context.tableandcontext.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:
SubStepError – If called outside a feature context.
AssertionError – If any sub-step fails (propagated from Behave).
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_stepwhich Behave checks before aborting a scenario on step failure.- Parameters:
enabled – When
True, scenarios keep running remaining steps even after a step fails. WhenFalse, execution stops at the first failing step (Behave’s default).- Raises:
BehaveKitError – If
enabledis 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_steptoTrueon 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_namecannot 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_namecannot 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.
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:
objectRead-only, deeply immutable resolved configuration.
- classmethod from_toml(path: str | Path, *, env: str | None = None, overrides: dict[str, str] | None = None) KitConfig[source]¶
Load a
KitConfigfrom a TOML file.envdefaults tobehave_kit.envif omitted. CLI--setoverrides are dotted keys flattened to top-level strings.- Raises:
ConfigError – if the file is missing, unreadable, malformed, or the requested profile does not exist.
- 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 tovar_type.Resolution order:
os.environ->context.config.raw(ifcontextis given) ->default-> EnvVarError (ifrequired).
- behave_kit.env.env_snapshot() Iterator[None][source]¶
Snapshot
os.environand 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 theenvprofile, and attach it tocontext.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 tovar_type.Resolution order:
os.environ->context.config.raw(ifcontextis given) ->default-> EnvVarError (ifrequired).
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 theenvprofile, and attach it tocontext.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_dictwith CLI--set key=valueoverrides 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_namehas 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.
Data¶
Test data loading: CSV/JSON/YAML/XLSX with caching and named providers.
- class behave_kit.data.DataCache[source]¶
Bases:
objectCache of loaded data, keyed by
(path, scope).
- behave_kit.data.data_provider(name: str) Callable[[Callable[[...], object]], Callable[[...], object]][source]¶
Register
funcas the data provider namedname.
- 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
pathand 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
pathand 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
pathand 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
pathand 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:
objectCache of loaded data, keyed by
(path, scope).
@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.
Fixtures¶
Tag-based fixtures with lifecycle and dependency resolution.
- class behave_kit.fixtures.FixtureManager[source]¶
Bases:
objectOrchestrates fixture setup/teardown lifecycle by scope.
- setup_for_feature(context: Context, feature: object) None[source]¶
Run feature-scoped fixtures matching
featuretags.
- 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
funcas the fixture namedname.- 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 receivingcontext.setup_fnis called immediately;teardown_fnis 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
funcas the fixture namedname.- 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
namein 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:
objectOrchestrates fixture setup/teardown lifecycle by scope.
- setup_for_feature(context: Context, feature: object) None[source]¶
Run feature-scoped fixtures matching
featuretags.
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
nametomatch_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
funcas the converter for parameter typename.patterndocuments 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,stepdecorator factories andregister()/clear()methods.- Raises:
StepError – If
default_matcheris 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 forcontextand 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 causeRuntimeError: 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
nametomatch_string.
- behave_kit.steps.parameters.get_parameter_type_pattern(name: str) str | None[source]¶
Return the registered pattern for
name, orNoneif not registered.
- behave_kit.steps.parameters.parameter_type(name: str, pattern: str = '') Callable[[Callable[[str], Any]], Callable[[str], Any]][source]¶
Register
funcas the converter for parameter typename.patterndocuments 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.contextis bound automatically — nocontextparameter 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,stepdecorator factories andregister()/clear()methods.- Raises:
StepError – If
default_matcheris not a valid matcher reference.
- behave_kit.steps.classes.teardown_steps(context: Context) None[source]¶
Call
teardown()on every live step-instance forcontextand 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 causeRuntimeError: 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_kitlogger.continue_after_failed – When
True, scenarios continue executing remaining steps after a failure. WhenFalse, the default Behave behaviour (stop on first failure) is restored.Noneleaves 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 priorsetup()(no-op). Also tears down any class-based step instances created during the scenario, even ifsetup()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
conditionuntil it is truthy ortimeoutseconds 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
conditiondoes not become truthy withintimeoutseconds.
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
pathwithin nesteddata, ordefault.pathuses 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
defaultis 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:
BehaveKitErrorRaised when a callable takes longer than the allowed time.
- behave_kit.timing.assert_under(seconds: float, func: Callable[[], R], *, message: str = '') R[source]¶
Run
funcand assert it completes withinseconds.- 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
functakes longer thanseconds.
- 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
secondsis 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.
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:
ExceptionBase exception for all behave-kit errors.
- exception behave_kit._core.errors.ConfigError(message: str, *, cause: BaseException | None = None, suggestion: str | None = None)[source]¶
Bases:
BehaveKitErrorRaised 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:
BehaveKitErrorRaised 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:
BehaveKitErrorRaised 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:
BehaveKitErrorRaised 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,AttributeErrorRaised 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:
BehaveKitErrorRaised 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:
BehaveKitErrorRaised 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:
ProtocolLoads test data from a file into a Python object.
- class behave_kit._core.types.DiffMatcher(*args, **kwargs)[source]¶
Bases:
ProtocolCompares two values of a specific type and reports differences.
- matches(actual: object, expected: object) object[source]¶
Compare
actualagainstexpectedand return a diff result.- Parameters:
actual – Value produced by the code under test.
expected – Expected value.
- Returns:
An object describing the comparison (e.g.
Trueif equal, or a structured diff otherwise).
- class behave_kit._core.types.FixtureFn(*args, **kwargs)[source]¶
Bases:
ProtocolSets up a resource for a scenario/feature and tears it down afterwards.
- class behave_kit._core.types.Scope(*values)[source]¶
Bases:
EnumLifetime 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:
ProtocolDecides whether a step or scenario should be skipped.
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:
objectRead-only, deeply immutable resolved configuration.
- classmethod from_toml(path: str | Path, *, env: str | None = None, overrides: dict[str, str] | None = None) KitConfig[source]¶
Load a
KitConfigfrom a TOML file.envdefaults tobehave_kit.envif omitted. CLI--setoverrides are dotted keys flattened to top-level strings.- Raises:
ConfigError – if the file is missing, unreadable, malformed, or the requested profile does not exist.
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
nameis 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
itemundername.- Raises:
FixtureError – if
nameis already registered.
- resolve_dependencies(name: str) list[str][source]¶
Return the dependency chain for
namein setup order.The returned list contains
name’s dependencies followed bynameitself, 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.
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.