An implementation of the Cucumber testing framework for Rust. Fully native, no external test runners or dependencies.
Create a directory called tests/
in your project root and create a test target of your choice. In this example we will name it cucumber.rs
.
Add this to your Cargo.toml
:
```toml [[test]] name = "cucumber" harness = false # Allows Cucumber to print output instead of libtest
[dev-dependencies] cucumber_rust = "^0.6.0" ```
Create a directory called features/
and put a feature file in it named something like example.feature
. It might look like:
```gherkin Feature: Example feature
Scenario: An example scenario Given I am trying out Cucumber When I consider what I am doing Then I am interested in ATDD And we can implement rules with regex
```
And here's an example of implementing those steps using our tests/cucumber.rs
file:
```rust
extern crate cucumber_rust;
pub struct MyWorld { // You can use this struct for mutable context in scenarios. foo: String }
impl cucumberrust::World for MyWorld {} impl std::default::Default for MyWorld { fn default() -> MyWorld { // This function is called every time a new scenario is started MyWorld { foo: "a default string".tostring() } } }
mod examplesteps { // Any type that implements cucumberrust::World + Default can be the world steps!(::MyWorld => { given "I am trying out Cucumber" |world, step| { world.foo = "Some string".to_string(); // Set up your context in given steps };
when "I consider what I am doing" |world, step| {
// Take actions
let new_string = format!("{}.", &world.foo);
world.foo = new_string;
};
then "I am interested in ATDD" |world, step| {
// Check that the outcomes to be observed have occurred
assert_eq!(world.foo, "Some string.");
};
then regex r"^we can (.*) rules with regex$" |world, matches, step| {
// And access them as an array
assert_eq!(matches[1], "implement");
};
then regex r"^we can also match (\d+) (.+) types$" (usize, String) |world, num, word, step| {
// `num` will be of type usize, `word` of type String
assert_eq!(num, 42);
assert_eq!(word, "olika");
};
then "we can use data tables to provide more parameters" |world, step| {
let table = step.table().unwrap().clone();
assert_eq!(table.header, vec!["key", "value"]);
let expected_keys = table.rows.iter().map(|row| row[0].to_owned()).collect::<Vec<_>>();
let expected_values = table.rows.iter().map(|row| row[1].to_owned()).collect::<Vec<_>>();
assert_eq!(expected_keys, vec!["a", "b"]);
assert_eq!(expected_values, vec!["fizz", "buzz"]);
};
});
}
// Declares a before handler function named a_before_fn
before!(abeforefn => |scenario| {
});
// Declares an after handler function named an_after_fn
after!(anafterfn => |scenario| {
});
// A setup function to be called before everything else fn setup() {
}
cucumber! {
features: "./features", // Path to our feature files
world: ::MyWorld, // The world needs to be the same for steps and the main cucumber call
steps: &[
examplesteps::steps // the steps!
macro creates a steps
function in a module
],
setup: setup, // Optional; called once before everything
before: &[
abeforefn // Optional; called before each scenario
],
after: &[
anafter_fn // Optional; called after each scenario
]
}
```
The cucumber!
creates the main
function to be run.
The steps!
macro generates a function named steps
with all the declared steps in the module
it is defined in. Ordinarily you would create something like a steps/
directory to hold your
steps modules instead of inline like the given example.
The full gamut of Cucumber's Gherkin language is implemented by the gherkin-rust project. Most features of the Gherkin language are parsed already and accessible via the relevant structs.
This project is licensed under either of
at your option.