Harness adapter core APIs

Version 0.6.0 Updated Sep 14, 2026

Architectural Decision Record (ADR-005) introduces a harness adapter layer, so framework integrations can live in opt-in crates. Phase 9.1 ships the core contracts in rstest-bdd-harness.

#[scenario] and scenarios! now support harness and attribute-policy macro parameters. The legacy runtime = "tokio-current-thread" argument in scenarios! remains supported as compatibility syntax for Tokio harness selection. The core crate remains immediately useful for adapter authors building Tokio, Graphical Processing User Interface (GPUI), or other harness plug-ins.

Defining a harness adapter

Use HarnessAdapter with ScenarioRunRequest<'a, C, T> and ScenarioRunner<'a, C, T> to execute one scenario closure inside the harness environment. C is the harness-specific context type:

use rstest_bdd_harness::{
    HarnessAdapter, HarnessResult, ScenarioMetadata, ScenarioRunRequest, ScenarioRunner,
};

struct MyHarness;

impl HarnessAdapter for MyHarness {
    type Context = ();

    fn run<T>(
        &self,
        request: ScenarioRunRequest<'_, Self::Context, T>,
    ) -> HarnessResult<T> {
        // Optional harness-specific setup using request.metadata().
        Ok(request.run_without_context())
    }
}

let request = ScenarioRunRequest::new(
    ScenarioMetadata::new(
        "tests/features/login.feature",
        "Successful login",
        12,
        vec!["@smoke".to_string()],
    ),
    ScenarioRunner::new(|()| "ok"),
);

let harness = MyHarness;
assert_eq!(harness.run(request).expect("harness should not fail"), "ok");

Harnesses that need framework resources can choose a non-unit context type and pass it through request.run(context). For example, a GPUI harness can use TestAppContext, and a Bevy harness can use bevy::ecs::World.

The built-in StdHarness implements the same trait and runs the closure synchronously without an async runtime or UI harness.

This user guide focuses on how to use the delivered harness API. The design document records the underlying trust model and architectural rationale, in particular the path-based first-party policy mapping and the associated Context handoff introduced by ADR-007.

Defining an attribute policy

AttributePolicy supplies test attributes that macro expansion should apply. The default policy emits only #[rstest::rstest]:

use rstest_bdd_harness::{AttributePolicy, DefaultAttributePolicy};

let attrs = DefaultAttributePolicy::test_attributes();
assert_eq!(attrs.len(), 1);
assert_eq!(attrs[0].render(), "#[rstest::rstest]");

Adapter crates can define custom policies with additional attributes, such as Tokio runtime options, while keeping those dependencies out of the core rstest-bdd crates. At present, macro code generation recognizes only the first-party canonical policy paths for Tokio and GPUI; unknown third-party policy paths fall back to #[rstest::rstest].

Verifying an attribute policy

rstest-bdd-harness ships the conformance check every adapter policy is expected to pass. Supply the attributes the policy should render, in order:

use rstest_bdd_harness::DefaultAttributePolicy;
use rstest_bdd_harness::policy_conformance::assert_attribute_policy_conformance;

assert_attribute_policy_conformance::<DefaultAttributePolicy>(
    &["#[rstest::rstest]"],
);

It pins three invariants, panicking with a descriptive message when any of them is violated:

  • Emit — the policy returns exactly as many attributes as expected.
  • Render — each attribute renders to the corresponding expected string, in the same order.
  • rstest is first — the leading attribute path is rstest::rstest, so fixture expansion runs before the runtime-specific test macro.

Call it from a single #[test] in your adapter crate rather than re-implementing the assertions; only the expected rendered attributes are adapter-specific.

One placement caveat: if the adapter's library target sets test = false in its manifest — as the GPUI adapter does — an in-module #[cfg(test)] block is never compiled, so the check would silently never run. Put the call in an integration test under tests/ in that case.