Skip to content

Cucumber testing framework for Rust. Fully native, no external test runners or dependencies.

License

Notifications You must be signed in to change notification settings

sravinet/cucumber

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

686 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Cucumber testing framework for Rust

crates.io Rust 1.87+ Unsafe Forbidden
CI Rust docs

An implementation of the Cucumber testing framework for Rust. Fully native, no external test runners or dependencies.

Usage

Describe testing scenarios in .feature files:

Feature: Eating too much cucumbers may not be good for you

  Scenario: Eating a few isn't a problem
    Given Alice is hungry
    When she eats 3 cucumbers
    Then she is full

Implement World trait and describe steps:

use std::time::Duration;

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

#[derive(Debug, Default, cucumber::World)]
struct World {
    user: Option<String>,
    capacity: usize,
}

#[given(expr = "{word} is hungry")] // Cucumber Expression
async fn someone_is_hungry(w: &mut World, user: String) {
    sleep(Duration::from_secs(2)).await;

    w.user = Some(user);
}

#[when(regex = r"^(?:he|she|they) eats? (\d+) cucumbers?$")]
async fn eat_cucumbers(w: &mut World, count: usize) {
    sleep(Duration::from_secs(2)).await;

    w.capacity += count;

    assert!(w.capacity < 4, "{} exploded!", w.user.as_ref().unwrap());
}

#[then("she is full")]
async fn is_full(w: &mut World) {
    sleep(Duration::from_secs(2)).await;

    assert_eq!(w.capacity, 3, "{} isn't full!", w.user.as_ref().unwrap());
}

#[tokio::main]
async fn main() {
    World::run("tests/features/readme").await;
}

Add test to Cargo.toml:

[[test]]
name = "readme"
harness = false  # allows Cucumber to print output instead of libtest

For more examples check out the Book (current | edge).

Working with DataTables

Cucumber DataTables allow you to pass structured data to your steps. This crate provides rich support for DataTables through the DataTable type:

Direct Parameter Support

You can receive DataTables directly as step parameters:

use cucumber::{given, DataTable, World};

#[derive(Debug, Default, World)]
struct MyWorld {
    products: Vec<Product>,
}

#[derive(Debug)]
struct Product {
    name: String,
    price: f64,
}

#[given("the following products:")]
fn load_products(world: &mut MyWorld, table: DataTable) {
    // Access rows with headers
    for row in table.rows_with_header().skip(1) {
        world.products.push(Product {
            name: row["name"].to_string(),
            price: row["price"].parse().unwrap(),
        });
    }
}

Optional DataTables

DataTables can also be optional in your steps:

#[given("a product")]
fn create_product(world: &mut MyWorld, table: Option<DataTable>) {
    if let Some(table) = table {
        // Use provided data
        let row = table.rows().nth(1).unwrap();
        world.products.push(Product {
            name: row[0].to_string(),
            price: row[1].parse().unwrap(),
        });
    } else {
        // Use defaults when no table provided
        world.products.push(Product {
            name: "Default".to_string(),
            price: 0.0,
        });
    }
}

Rich API

The DataTable type provides multiple ways to access your data:

// Access as rows
for row in table.rows() {
    println!("{:?}", row);
}

// Access with headers as HashMap
for row in table.rows_with_header().skip(1) {
    println!("{} costs {}", row["name"], row["price"]);
}

// Transpose the table
let transposed = table.transpose();

// Access specific cells
if let Some(cell) = table.cell(1, 0) {
    println!("Cell value: {}", cell);
}

// Get column values
let names: Vec<String> = table.column(0)
    .map(|s| s.to_string())
    .collect();

Feature File Example

Feature: Product Management

  Scenario: Loading products
    Given the following products:
      | name    | price |
      | Apple   | 1.20  |
      | Banana  | 0.50  |
      | Orange  | 0.80  |
    When I calculate the total
    Then the total should be 2.50

Cargo features

Supporting crates

The full gamut of Cucumber's Gherkin language is implemented by the gherkin crate. Most features of the Gherkin language are parsed already and accessible via the relevant structs.

Known issues

  • Scenario Outline is treated the same as Outline or Example in the parser (gherkin/#19).

License

This project is licensed under either of

at your option.

About

Cucumber testing framework for Rust. Fully native, no external test runners or dependencies.

Resources

License

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Rust 89.2%
  • JavaScript 9.1%
  • Gherkin 1.4%
  • Makefile 0.3%