Scenario Outline keyword

The Scenario Outline keyword can be used to run the same scenario multiple times, with different combinations of values.

Feature: Animal feature

  Scenario Outline: If we feed a hungry animal it will no longer be hungry
    Given a hungry <animal>
    When I feed the <animal> <n> times
    Then the <animal> is not hungry

    | animal | n |
    | cat    | 2 |
    | dog    | 3 |
    | 🦀     | 4 |

At parsing stage <template>s are replaced by value from cells, so we may get that value in step matching functions (if we need though).

NOTE: <template>s are replaced even inside doc strings and data tables.

extern crate cucumber;
extern crate tokio;

use std::{collections::HashMap, time::Duration};

use cucumber::{given, then, when, World};
use tokio::time::sleep;

#[derive(Debug, Default)]
struct Animal {
    pub hungry: bool,

impl Animal {
    fn feed(&mut self) {
        self.hungry = false;

#[derive(Debug, Default, World)]
pub struct AnimalWorld {
    animals: HashMap<String, Animal>,

#[given(regex = r"^a (hungry|satiated) (\S+)$")]
async fn hungry_animal(world: &mut AnimalWorld, state: String, which: String) {

    world.animals.entry(which).or_insert(Animal::default()).hungry =
        match state.as_str() {
            "hungry" => true,
            "satiated" => false,
            _ => unreachable!(),

#[when(expr = "I feed the {word} {int} time(s)")]
async fn feed_animal(world: &mut AnimalWorld, which: String, times: usize) {

    for _ in 0..times {

#[then(expr = "the {word} is not hungry")]
async fn animal_is_fed(world: &mut AnimalWorld, which: String) {

    assert!(!world.animals.get(&which).map_or(true, |a| a.hungry));

async fn main() {

NOTE: Scenario Outline runs the whole scenario for each table row separately, unlike data tables, which run the whole table inside a single step.