Struct faux::When

source ·
pub struct When<'m, R, I, O, M: InvocationMatcher<I>> { /* private fields */ }
Expand description

Provides methods to stub the implementation or return value of the stubbed method.

Created using when!.

By default, methods are stubbed for all invocations. Use when! for an ergonomic way to set argument matchers. For more features, use with_args.

By default, all methods are stubbed indefinitely. Thus, any stubbed values needs to be cloneable and any stubbed implementation cannot consume variables. Use the times and once methods to override these defaults.

Do NOT rely on the signature of When. While changing the methods of When will be considered a breaking change, changing the generics within When will not.

Implementations§

source§

impl<'m, R, I, O, M: InvocationMatcher<I> + Send + 'static> When<'m, R, I, O, M>

source

pub fn then_return(self, value: O)where O: Send + Clone + 'static,

Sets the return value of the stubbed method.

Requires the value to be static. For a more lax but unsafe alternative, use then_unchecked_return.

The returned value will be cloned on each invocation of the stub. Using this method to return another mock instance will fail unless that stub instance has its clone method already stubbed.

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

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

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

  faux::when!(mock.multi_args).then_return(5);
  // mock activates multiple times
  assert_eq!(mock.multi_args(&2, 3), 5);
  assert_eq!(mock.multi_args(&2, 3), 5);
}
source

pub fn then(self, stub: impl FnMut(I) -> O + 'static + Send)where O: 'static,

Sets the implementation of the stubbed method to the provided closure.

The input to the closure is a tuple of all its non-receiver parameters.

The provided closure can only capture static variables and it must be stubbing a method with static output. For a more lax but unsafe alternative, use then_unchecked.

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

#[faux::methods]
impl Foo {
    pub fn no_args(&mut self) -> i32 {
      /* implementation code */
    }

    pub fn single_arg(&self, a: u8) -> Vec<i8> {
      /* implementation code */
    }

    pub fn multi_args(self, a: &i32, b: i8) -> u32 {
      /* implementation code */
    }

    pub fn out_ref(&self) -> &u32 {
      /* implementation code */
    }
}

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

  // method with no params
  faux::when!(mock.no_args).then(|_| 5);
  assert_eq!(mock.no_args(), 5);

  // method with a single param
  faux::when!(mock.single_arg).then(|input| vec![input as i8]);
  assert_eq!(mock.single_arg(8), vec![8]);

  // method with multiple params - some can be references
  faux::when!(mock.multi_args).then(|(&a, _)| a as u32);
  assert_eq!(mock.multi_args(&5, 2), 5);

  // cannot stub methods that return references
  // let x = 5;
  // faux::when!(mock.out_ref).then(|_| &x);
}
source

pub unsafe fn then_unchecked_return(self, value: O)where O: Send + Clone,

Analog of then_return that allows stubbing non-static return values.

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

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

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

  let x = 5;
  unsafe { faux::when!(mock.multi_args).then_unchecked_return(&x) }
  assert_eq!(*mock.multi_args(&2, 3), x);
}
Safety

The lifetime of the returned object is not checked and can cause memory safety issues if used incorrectly.

If the owner of the borrowed data is dropped while the captured reference is still accessible, a use-after-free violation will be triggered.

This method can also cause aliasing issues where multiple mutable references are held for the same object.

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

#[faux::methods]
impl Foo {
    pub fn out_ref(&self) -> &u32 {
      /* implementation code */
    }
}

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

  let x = 5;
  unsafe { faux::when!(mock.out_ref).then_unchecked_return(&x) }
  std::mem::drop(x);
  // assert_eq!(*mock.ref_return(), 5); // <~~ UB: use after free
}
source

pub unsafe fn then_unchecked(self, stub: impl FnMut(I) -> O + Send)

Analog of then that allows stubbing implementations with non-static closures.

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

#[faux::methods]
impl Foo {
    pub fn out_ref(&mut self) -> &i32 {
      /* implementation code */
    }
}

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

  // the output can be a reference to the environment
  // but this can be *very* dangerous
  let x = 5;
  unsafe { faux::when!(mock.out_ref).then_unchecked(|_| &x) }
  assert_eq!(*mock.out_ref(), x);
}
Safety

The lifetimes of the outputs and captured variables are not checked. While this gives the caller maximum flexibility when mocking, it is not memory safe when used incorrectly.

If the mocked method is called after its captured variables are dropped then a use-after-free violation will be triggered.

Relationships between inputs, outputs, and captured variable lifetimes are lost. This allows for easy violations of Rust’s aliasing checks, creating undefined behavior.

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

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

fn main() {
  let mut mock = Foo::faux();
  // the output and input references are the same
  unsafe { faux::when!(mock.out_ref).then_unchecked(|i| i) }

  let mut x = 5;
  // y (the output) is a mutable reference back to x (the input)
  // but there is no compile-time link between the two
  let y = mock.out_ref(&mut x);

  // x and y are pointing to the same data!
  assert_eq!(*y, 5);
  assert_eq!(x, 5);

  // changes in x are reflected in y and vice versa
  // this is UB and is not allowed in safe Rust!
  x += 1;
  assert_eq!(x, 6);
  assert_eq!(*y, 6);
}
source

pub fn times(self, times: usize) -> Self

Limits the number of calls for which a mock is active.

Calls past the limit will result in a panic.

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

#[faux::methods]
impl Foo {
    pub fn single_arg(&self, a: u8) -> Vec<i8> {
      /* implementation code */
    }
}

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

  // limit to 5 calls
  faux::when!(mock.single_arg)
      .times(5)
      .then(|input| vec![input as i8]);

  // can be called 5 times
  for _ in 0..5 {
    assert_eq!(mock.single_arg(8), vec![8]);
  }
}
Panics

Panics if the mock is called more times than specified.

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

#[faux::methods]
impl Foo {
    pub fn single_arg(&self, a: u8) -> Vec<i8> {
      /* implementation code */
    }
}

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

  // limit to 5 calls
  faux::when!(mock.single_arg)
      .times(5)
      .then(|input| vec![input as i8]);

  // panics on the 6th call
  for _ in 0..6 {
    assert_eq!(mock.single_arg(8), vec![8]);
  }
}
source

pub fn once(self) -> Once<'m, R, I, O, M>

Limits mock to one call, allowing mocks to consume captured variables.

Panics if the mock is called more than once.

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

#[faux::methods]
impl Foo {
    pub fn single_arg(&self, a: u8) -> Vec<i8> {
      /* implementation code */
    }
}

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

  let vec = vec![25];
  // moves vec to the closure
  faux::when!(mock.single_arg).once().then(|_| vec);
  assert_eq!(mock.single_arg(8), vec![25]);
}
Panics

Panics if the mock is called more than once.

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

#[faux::methods]
impl Foo {
    pub fn single_arg(&self, a: u8) -> Vec<i8> {
      /* implementation code */
    }
}

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

  let vec = vec![25];
  faux::when!(mock.single_arg).once().then(|_| vec);
  assert_eq!(mock.single_arg(8), vec![25]);
  //panics on its second call
  mock.single_arg(8);
}
source

pub fn with_args<N: InvocationMatcher<I> + Send + 'static>( self, matcher: N ) -> When<'m, R, I, O, N>

Specifies a matcher for the invocation.

This lets you pass matchers for each method argument.

See when! for an ergonomic way to pass the matcher.

If all arguments implement Debug, a tuple of ArgMatchers can be provided where each ArgMatcher matches an individual argument.

If the method only has a single argument, use a tuple of a single element: (ArgMatcher,)

For more complex cases, you may pass a custom InvocationMatcher.

Auto Trait Implementations§

§

impl<'m, R, I, O, M> RefUnwindSafe for When<'m, R, I, O, M>where M: RefUnwindSafe,

§

impl<'m, R, I, O, M> Send for When<'m, R, I, O, M>where M: Send,

§

impl<'m, R, I, O, M> Sync for When<'m, R, I, O, M>where M: Sync,

§

impl<'m, R, I, O, M> Unpin for When<'m, R, I, O, M>where M: Unpin,

§

impl<'m, R, I, O, M> !UnwindSafe for When<'m, R, I, O, M>

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.