Skip to content

Guides · 4 of 7

Bind and filter scenarios

A scenario becomes a test when something binds it: #[scenario] for one, scenarios! for a directory. Tags decide which, and skip! says why one did not run.

One scenario

#[scenario] turns a function into the test for one scenario. path names the feature file, relative to the crate root; name picks a scenario by title, or index by position, and without either the first scenario is used.

tests/hamper.rs
#[scenario(path = "tests/features/hamper.feature", name = "Pack the picnic")]
fn pack_the_picnic(hamper: Hamper) {
    let _ = hamper;
}

Compiled and run against rstest-bdd 0.6.0.

The function’s parameters are the scenario’s fixtures, and its body runs after the last step. A name that matches no scenario, or more than one, is a compile error.

A whole directory

scenarios! walks a directory and generates a test for every scenario it finds, named from the file and the scenario title. Fixtures the steps need are listed with their types, since there is no function signature to declare them.

tests/weather.rs
//! The `scenarios!` macro auto-discovering feature files, tag filtering, and
//! a step that skips a scenario at runtime.

use rstest::fixture;
use rstest_bdd_macros::{given, scenarios, then};

#[derive(Default)]
struct Evening {
    forecast: String,
}

#[fixture]
fn evening() -> Evening {
    Evening::default()
}

#[given("the forecast is \"{forecast}\"")]
fn forecast(evening: &mut Evening, forecast: String) {
    if forecast == "thunder" {
        rstest_bdd::skip!("no picnics in a thunderstorm");
    }
    evening.forecast = forecast;
}

#[then("the blanket goes on the grass")]
fn blanket(evening: &Evening) {
    assert_eq!(evening.forecast, "dry");
}

#[then("the lanterns are lit early")]
fn lanterns(evening: &Evening) {
    assert_eq!(evening.forecast, "fog");
}

scenarios!(
    "tests/features/auto",
    tags = "@smoke and not @wip",
    fixtures = [evening: Evening]
);

Compiled and run against rstest-bdd 0.6.0.

cargo test --test weatherrstest-bdd 0.6.0
running 2 tests
test auto_scenarios::weather_thunder_in_the_hills ... ok
test auto_scenarios::weather_a_dry_evening ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Tag expressions

tags = "…" filters by the tags on the feature, the scenario, and an Outline’s Examples table. Expressions use and, or, not, and parentheses, with not binding tightest and or loosest.

tests/features/auto/weather.feature
Feature: Picnic weather
  @smoke
  Scenario: A dry evening
    Given the forecast is "dry"
    Then the blanket goes on the grass

  @wip
  Scenario: Fog over the loch
    Given the forecast is "fog"
    Then the lanterns are lit early

  @smoke @allow_skipped
  Scenario: Thunder in the hills
    Given the forecast is "thunder"
    Then the blanket goes on the grass

Compiled and run against rstest-bdd 0.6.0.

"@smoke and not @wip" keeps the dry evening and the thunder, and leaves the fog out; there are two tests above, not three. The filter runs when the test compiles, so a left-out scenario is absent from the test list rather than ignored. #[scenario] takes the same tags argument, and fails to compile when the scenario it names does not satisfy it.

Skipping

A step that cannot sensibly go on calls rstest_bdd::skip! with a reason. The remaining steps and the scenario body do not run. The thunder scenario above does exactly that:

tests/weather.rs
#[given("the forecast is \"{forecast}\"")]
fn forecast(evening: &mut Evening, forecast: String) {
    if forecast == "thunder" {
        rstest_bdd::skip!("no picnics in a thunderstorm");
    }
    evening.forecast = forecast;
}

Compiled and run against rstest-bdd 0.6.0.

A skipped scenario still reports ok, which is easy to miss. Set RSTEST_BDD_FAIL_ON_SKIPPED=1 and every skip fails the test, unless its feature or scenario carries @allow_skipped, as the thunder does.

Terminal
$ RSTEST_BDD_FAIL_ON_SKIPPED=1 cargo test --test weather

cargo bdd skipped --reasons lists every skip and its reason; see tooling.

Fine print