Crate rstest_reuse
source · [−]Expand description
Reuse rstest
’s parametrized cases
This crate give a way to define a tests set and apply them to every case you need to test.
With rstest
crate you can define a tests list but if you want to apply the same tests
to another test function you must rewrite all cases or write some macros that do the job.
Both solutions have some drawbreak:
- introduce duplication
- macros makes code harder to read and shift out the focus from tests core
The aim of this crate is solve this problem. rstest_reuse
expose two attributes:
#[template]
: to define a template#[apply]
: to apply a defined template to create tests
Here is a simple example:
use rstest::rstest;
use rstest_reuse::{self, *};
// Here we define the template. This define
// * The test list name to `two_simple_cases`
// * cases: here two cases that feed the `a`, `b` values
#[template]
#[rstest]
#[case(2, 2)]
#[case(4/2, 2)]
fn two_simple_cases(#[case] a: u32,#[case] b: u32) {}
// Here we apply the `two_simple_cases` template: That is expanded in
// #[rstest]
// #[case(2, 2)]
// #[case(4/2, 2)]
// fn it_works(#[case] a: u32,#[case] b: u32) {
// assert!(a == b);
// }
#[apply(two_simple_cases)]
fn it_works(a: u32, b: u32) {
assert!(a == b);
}
// Here we reuse the `two_simple_cases` template to create two
// other tests
#[apply(two_simple_cases)]
fn it_fail(a: u32, b: u32) {
assert!(a != b);
}
If we run cargo test
we have:
Finished test [unoptimized + debuginfo] target(s) in 0.05s
Running target/debug/deps/playground-8a1212f8b5eb00ce
running 4 tests
test it_fail::case_1 ... FAILED
test it_works::case_1 ... ok
test it_works::case_2 ... ok
test it_fail::case_2 ... FAILED
failures:
---- it_fail::case_1 stdout ----
-------------- TEST START --------------
thread 'it_fail::case_1' panicked at 'assertion failed: a != b', src/main.rs:34:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
---- it_fail::case_2 stdout ----
-------------- TEST START --------------
thread 'it_fail::case_2' panicked at 'assertion failed: a != b', src/main.rs:34:5
failures:
it_fail::case_1
it_fail::case_2
test result: FAILED. 2 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out
error: test failed, to rerun pass '--bin playground'
Simple and neat!
Note that if the test arguments names match the template’s ones you can don’t repeate the arguments attributes.
Composition and Values
If you need to add some cases or values when apply a template you can leverage on composition. Here a simple example:
use rstest::rstest;
use rstest_reuse::{self, *};
#[template]
#[rstest]
#[case(2, 2)]
#[case(4/2, 2)]
fn base(#[case] a: u32, #[case] b: u32) {}
// Here we add a new case and an argument in a value list:
#[apply(base)]
#[case(9/3, 3)]
fn it_works(a: u32, b: u32, #[values("a", "b")] t: &str) {
assert!(a == b);
assert!("abcd".contains(t))
}
cargo test
runs 6 tests:
running 6 tests
test it_works::case_1::t_2 ... ok
test it_works::case_2::t_2 ... ok
test it_works::case_2::t_1 ... ok
test it_works::case_3::t_2 ... ok
test it_works::case_3::t_1 ... ok
test it_works::case_1::t_1 ... ok
Template can also used for #[values]
and #[with]
arguments if you need:
use rstest::*;
use rstest_reuse::{self, *};
#[template]
#[rstest]
fn base(#[with(42)] fix: u32, #[values(1,2,3)] v: u32) {}
#[fixture]
fn fix(#[default(0)] inner: u32) -> u32 {
inner
}
#[apply(base)]
fn use_it_with_fixture(fix: u32, v: u32) {
assert!(fix%v == 0);
}
#[apply(base)]
fn use_it_without_fixture(v: u32) {
assert!(24 % v == 0);
}
cargo test
runs 6 tests:
running 6 tests
test use_it_with_fixture::v_1 ... ok
test use_it_without_fixture::v_1 ... ok
test use_it_with_fixture::v_3 ... ok
test use_it_without_fixture::v_2 ... ok
test use_it_without_fixture::v_3 ... ok
test use_it_with_fixture::v_2 ... ok
Cavelets
use rstest_reuse
at the top of your crate
You should add use rstest_reuse
at the top of your crate:
#[cfg(test)]
use rstest_reuse;
This is due rstest_reuse::template
define a macro that need to call a rstest_reuse
’s macro.
I hope to remove this in the future but for now we should live with it.
Note that
use rstest_reuse::*;
is not enougth: this statment doesn’t include rstest_reuse
but just its public items.
Disclamer
This crate is in developer stage. I don’t know if I’ll include it in rstest
or changing some syntax in
the future.
I did’t test it in a lot of cases: if you have some cases where it doesn’t works file a ticket on
rstest
Attribute Macros
Apply a defined template. The function signature should satisfy the template attributes but can also add some other fixtures. Example:
Define a template where the name is given from the function name. This attribute register all
attributes. The function signature don’t really mater but to make it clear is better that you
use a signature like if you’re wrinting a standard rstest
.