Asserting

There are two ways of doing assertions in a step matching function:

Panic

Throwing a panic in a step matching function makes the appropriate step failed:

extern crate cucumber;
extern crate tokio;

use cucumber::{given, then, when, World};

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

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

#[derive(Debug, Default, World)]
pub struct AnimalWorld {
    cat: Cat,
}

#[given(regex = r"^a (hungry|satiated) cat$")]
fn hungry_cat(world: &mut AnimalWorld, state: String) {
    match state.as_str() {
        "hungry" =>  world.cat.hungry = true,
        "satiated" =>  world.cat.hungry = false,
        _ => unreachable!(),
    }
}

#[when("I feed the cat")]
fn feed_cat(world: &mut AnimalWorld) {
    world.cat.feed();
}

#[then("the cat is not hungry")]
fn cat_is_fed(_: &mut AnimalWorld) {
    panic!("Cats are always hungry!")
}

#[tokio::main]
async fn main() {
    AnimalWorld::cucumber()
        .run_and_exit("tests/features/book/writing/asserting.feature")
        .await;
}

record

NOTE: Failed step prints its location in a .feature file and the captured assertion message.

TIP: To additionally print the state of the World at the moment of failure, increase output verbosity via -vv CLI option.

TIP: By default, unlike unit tests, failed steps don't terminate the execution instantly, and the whole test suite is executed regardless of them. Use --fail-fast CLI option to stop execution on first failure.

Result and ?

Similarly to using the ? operator in Rust tests, we may also return a Result<()> from a step matching function, so returning an Err will cause the step to fail (anything implementing Display is sufficient).

extern crate cucumber;
extern crate tokio;

use cucumber::{given, then, when, World};

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

#[derive(Debug, Default, World)]
pub struct AnimalWorld {
    cat: Cat,
}

#[given(regex = r"^a (hungry|satiated) cat$")]
fn hungry_cat(world: &mut AnimalWorld, state: String) {
    match state.as_str() {
        "hungry" =>  world.cat.hungry = true,
        "satiated" =>  world.cat.hungry = false,
        _ => unreachable!(),
    }
}

#[when("I feed the cat")]
fn feed_cat(_: &mut AnimalWorld) {}

#[then("the cat is not hungry")]
fn cat_is_fed(world: &mut AnimalWorld) -> Result<(), &'static str> {
    (!world.cat.hungry).then_some(()).ok_or("Cat is still hungry!")
}

#[tokio::main]
async fn main() {
    AnimalWorld::cucumber()
        .run_and_exit("tests/features/book/writing/asserting.feature")
        .await;
}

record