Macro faux::when

source ·
when!() { /* proc-macro */ }
Expand description

Creates a When instance to stub a specific method in a struct.

Callers may specify argument matchers to limit the arguments for which the method is stubbed. Matchers can only be specified if all arguments implement Debug. The debug message is printed if any of the arguments fail to match.

The method to stub must be be in an impl blocked tagged by #[methods].

Examples

#[faux::create]
pub struct Foo {}

#[faux::methods]
impl Foo {
    pub fn some_method(&self, a: u32, b: i8) -> i32 {
        /* implementation code */
    }
}

fn main() {
    let mut mock = Foo::faux();

    // specify all arguments
    faux::when!(mock.some_method(8, 9)).then_return(10);
    // actual method calls have to match expectations
    assert_eq!(mock.some_method(8, 9), 10);
    // mock.some_method(1, 1); // <~~ panics - arguments do not match

    // check only the second argument
    faux::when!(mock.some_method(_, 4)).then_return(20);
    // only the second argument is being matched against
    // so the first argument could be anything
    assert_eq!(mock.some_method(999, 4), 20);
    assert_eq!(mock.some_method(123, 4), 20);
    // mock.some_method(999, 3); // <~~ panics - second argument does not match

    // no argument matchers
    faux::when!(mock.some_method).then_return(3);
    // the arguments do not matter at all
    assert_eq!(mock.some_method(1337, 20), 3);
    assert_eq!(mock.some_method(4, 5), 3);
    assert_eq!(mock.some_method(7, 6), 3);
}

An argument mismatch would look something like:

thread 'main' panicked at 'failed to call stub on 'Foo::some_method':
✗ Arguments did not match
  Expected: [8, 9]
  Actual:   [1, 1]

  Argument 0:
    Expected: 8
    Actual:   1
  Argument 1:
    Expected: 9
    Actual:   1

Argument Matchers

Argument matchers are specified by passing them to when!:

faux::when!(my_struct.my_method(/* matchers here */));

This rougly translates to:

faux::when!(my_struct.my_method).with_args((/* matchers here */));

Matcher syntax

To make argument matching easy to use, when! provides some syntactic sugar that converts given arguments to the appropiate ArgMatcher and passes them to with_args. If this proves difficult in your use case, you can use with_args directly.

Each of the following specify an equivalent ArgMatcher for a single argument:

when! argArgMatcher
{expr}eq({expr})
_any()
_ == {expr}eq_against({expr})
_ = {matcher}{matcher}

Replace _ with *_ in the last two rows to match against references. More specifically, this converts the matcher from ArgMatcher<T> into ArgMatcher<&T> using into_ref_matcher.

Examples

#[faux::create]
pub struct MyStruct;
#[faux::methods]
impl MyStruct {
    pub fn my_method(&self, a: &i32, b: i32) -> i32 {
       panic!()
   }
}

let mut my_struct = MyStruct::faux();

// the eq matcher works even though the first argument is a reference
// the `_` matcher will match any argument
faux::when!(my_struct.my_method(3, _)).then_return(4);
assert_eq!(my_struct.my_method(&3, 20), 4);

// a type that implements `PartialEq<i32>` but is not an `i32`
#[derive(Debug)]
struct OtherNumber(i64);

impl PartialEq<i32> for OtherNumber {
    fn eq(&self, rhs: &i32) -> bool {
        self.0 == *rhs as i64
    }
}

// `_ == {expr}` to test equality of different types
// `*_ == {expr}` to dereference an argument before matching
faux::when!(my_struct.my_method(
    *_ == OtherNumber(5),
    _ == OtherNumber(20),
)).then_return(8);
assert_eq!(my_struct.my_method(&5, 20), 8);

// `_ = {matcher}` will pass the matcher to `with_args` as written
// `*_ = {matcher}` will match against a dereferenced argument
faux::when!(my_struct.my_method(
    *_ = faux::matcher::eq_against(OtherNumber(4)),
    _ = faux::matcher::eq(9),
)).then_return(20);
assert_eq!(my_struct.my_method(&4, 9), 20);

// pattern! and from_fn! are allowed just as any other matcher
faux::when!(my_struct.my_method(
    *_ = faux::pattern!(10..=20),
    _ = faux::from_fn!(|arg: &i32| *arg > 50),
)).then_return(80);
assert_eq!(my_struct.my_method(&11, 60), 80);