#[fixture]
Expand description
Define a fixture that you can use in all rstest
’s test arguments. You should just mark your
function as #[fixture]
and then use it as a test’s argument. Fixture functions can also
use other fixtures.
Let’s see a trivial example:
use rstest::*;
#[fixture]
fn twenty_one() -> i32 { 21 }
#[fixture]
fn two() -> i32 { 2 }
#[fixture]
fn injected(twenty_one: i32, two: i32) -> i32 { twenty_one * two }
#[rstest]
fn the_test(injected: i32) {
assert_eq!(42, injected)
}
If the fixture function is an async
function your fixture become an async
fixture.
Default values
If you need to define argument default value you can use #[default(expression)]
argument’s attribute:
use rstest::*;
#[fixture]
fn injected(
#[default(21)]
twenty_one: i32,
#[default(1 + 1)]
two: i32
) -> i32 { twenty_one * two }
#[rstest]
fn the_test(injected: i32) {
assert_eq!(42, injected)
}
The expression
could be any valid rust expression, even an async
block if you need.
Moreover, if the type implements FromStr
trait you can use a literal string to build it.
#[fixture]
fn db_connection(
#[default("127.0.0.1:9000")]
addr: SocketAddr
) -> DbConnection {
// create connection
}
Async
If you need you can write async
fixtures to use in your async
tests. Simply use async
keyword for your function and the fixture become an async
fixture.
use rstest::*;
#[fixture]
async fn async_fixture() -> i32 { 42 }
#[rstest]
async fn the_test(#[future] async_fixture: i32) {
assert_eq!(42, async_fixture.await)
}
The #[future]
argument attribute helps to remove the impl Future<Output = T>
boilerplate.
In this case the macro expands it in:
#[rstest]
async fn the_test(async_fixture: impl std::future::Future<Output = i32>) {
assert_eq!(42, async_fixture.await)
}
If you need, you can use #[future]
attribute also with an implicit lifetime reference
because the macro will replace the implicit lifetime with an explicit one.
Rename
Sometimes you want to have long and descriptive name for your fixture but you prefer to use a much
shorter name for argument that represent it in your fixture or test. You can rename the fixture
using #[from(short_name)]
attribute like following example:
use rstest::*;
#[fixture]
fn long_and_boring_descriptive_name() -> i32 { 42 }
#[rstest]
fn the_test(#[from(long_and_boring_descriptive_name)] short: i32) {
assert_eq!(42, short)
}
#[once]
Fixture
Expecially in integration tests there are cases where you need a fixture that is called just once
for every tests. rstest
provides #[once]
attribute for these cases.
If you mark your fixture with this attribute, then rstest
will compute a static reference to your
fixture result and return this reference to all your tests that need this fixture.
In follow example all tests share the same reference to the 42
static value.
use rstest::*;
#[fixture]
#[once]
fn once_fixture() -> i32 { 42 }
// Take care!!! You need to use a reference to the fixture value
#[rstest]
#[case(1)]
#[case(2)]
fn cases_tests(once_fixture: &i32, #[case] v: i32) {
// Take care!!! You need to use a reference to the fixture value
assert_eq!(&42, once_fixture)
}
#[rstest]
fn single(once_fixture: &i32) {
assert_eq!(&42, once_fixture)
}
There are some limitations when you use #[once]
fixture. rstest
forbid to use once fixture
for:
async
function- Generic function (both with generic types or use
impl
trait)
Take care that the #[once]
fixture value will never be dropped.
Partial Injection
You can also partialy inject fixture dependency using #[with(v1, v2, ..)]
attribute:
use rstest::*;
#[fixture]
fn base() -> i32 { 1 }
#[fixture]
fn first(base: i32) -> i32 { 1 * base }
#[fixture]
fn second(base: i32) -> i32 { 2 * base }
#[fixture]
fn injected(first: i32, #[with(3)] second: i32) -> i32 { first * second }
#[rstest]
fn the_test(injected: i32) {
assert_eq!(-6, injected)
}
Note that injected value can be an arbitrary rust expression. #[with(v1, ..., vn)]
attribute will inject v1, ..., vn
expression as fixture arguments: all remaining arguments
will be resolved as fixtures.
Sometimes the return type cannot be infered so you must define it: For the few times you may
need to do it, you can use the #[default(type)]
, #[partial_n(type)]
function attribute
to define it:
use rstest::*;
#[fixture]
pub fn i() -> u32 {
42
}
#[fixture]
pub fn j() -> i32 {
-42
}
#[fixture]
#[default(impl Iterator<Item=(u32, i32)>)]
#[partial_1(impl Iterator<Item=(I,i32)>)]
pub fn fx<I, J>(i: I, j: J) -> impl Iterator<Item=(I, J)> {
std::iter::once((i, j))
}
#[rstest]
fn resolve_by_default(mut fx: impl Iterator<Item=(u32, i32)>) {
assert_eq!((42, -42), fx.next().unwrap())
}
#[rstest]
fn resolve_partial(#[with(42.0)] mut fx: impl Iterator<Item=(f32, i32)>) {
assert_eq!((42.0, -42), fx.next().unwrap())
}
partial_i
is the fixture used when you inject the first i
arguments in test call.
Old compact syntax
There is also a compact form for all previous features. This will mantained for a long time
but for fixture
I strongly recomand to migrate your code because you’ll pay a little
verbosity but get back a more readable code.
Follow the previous examples in old compact syntax.
Default
#[fixture(twenty_one=21, two=2)]
fn injected(twenty_one: i32, two: i32) -> i32 { twenty_one * two }
Rename
#[fixture]
fn long_and_boring_descriptive_name() -> i32 { 42 }
#[rstest(long_and_boring_descriptive_name as short)]
fn the_test(short: i32) {
assert_eq!(42, short)
}
Partial Injection
#[fixture(second(-3))]
fn injected(first: i32, second: i32) -> i32 { first * second }
Partial Type Injection
#[fixture(::default<impl Iterator<Item=(u32, i32)>>::partial_1<impl Iterator<Item=(I,i32)>>)]
pub fn fx<I, J>(i: I, j: J) -> impl Iterator<Item=(I, J)> {
std::iter::once((i, j))
}