#[methods]Expand description
Transforms methods in an impl block into mockable versions of
themselves.
Mockable methods can be mocked using when!.
Associated functions and private methods cannot be mocked. Calls to them are proxied to the real implementation.
§Requirements
The struct definition must have been tagged with
#[create].
§Examples
// #[faux::create] is a pre-req of #[faux::methods]
#[cfg_attr(test, faux::create)]
pub struct MyStruct {
/* fields */
}
// make every public method mockable
#[cfg_attr(test, faux::methods)]
impl MyStruct {
pub fn new(data: Vec<u32>) -> Self {
/* implementation code */
}
pub fn get(&self) -> usize {
20
}
}
use std::io::{self, Read};
// make every method mockable
#[cfg_attr(test, faux::methods)]
impl Read for MyStruct {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
/* implementation code */
}
}
// #[methods] will not mock associated functions
// thus allowing you to still create real instances
let real = MyStruct::new(vec![5]);
assert_eq!(real.get(), 20);
// mock instances need to be mutable
let mut fake = MyStruct::faux();
faux::when!(fake.get).then_return(3);
assert_eq!(fake.get(), 3);
faux::when!(fake.read).then(|a| Ok(a[0] as usize));
assert_eq!(fake.read(&mut vec![10]).unwrap(), 10);§Attribute arguments
§path
Indicates that the struct came from an imported module.
§Examples
mod foo {
#[faux::create]
pub struct MyStruct {}
// no need to tell #[faux::methods] where to find `MyStruct`
// if defined in the same module
#[faux::methods]
impl MyStruct {
pub fn three(&self) -> i32 {
3
}
}
mod foo_inner {
// the type is being imported
// so we have to tell faux where it came from
use super::MyStruct;
#[faux::methods(path = "super")]
impl MyStruct {
pub fn four(&self) -> i32 {
self.three() + 1
}
}
}
}
mod bar {
// the module is being imported
// so we have to tell faux where it came from
use crate::foo;
#[faux::methods(path = "crate")]
impl foo::MyStruct {
pub fn five(&self) -> i32 {
self.three() + 2
}
}
}
let mut x = foo::MyStruct::faux();
faux::when!(x.three).then_return(30);
faux::when!(x.four).then_return(40);
faux::when!(x.five).then_return(50);
assert_eq!(x.three(), 30);
assert_eq!(x.four(), 40);
assert_eq!(x.five(), 50);§self_type
Tells the attribute how real instances of the mockable struct are
being stored by #[create]. Read the docs in
create for more information.
Because this argument specifies how the struct is stored, every
mockable method must use a receiver that can be obtained from
the specified self_type. For example, if self_type = Rc, then
a &self or an self: Rc<Self> can be used as receivers, but a
&mut self cannot. If you need an unsupported combination, please
file an issue.
§Examples
use std::rc::Rc;
#[faux::create(self_type = "Rc")]
pub struct ByRc {}
#[faux::methods(self_type = "Rc")]
impl ByRc {
// you can return an owned instance
pub fn new() -> Self {
ByRc {}
}
// or an instance wrapped in the `self_type`
pub fn new_rc() -> Rc<Self> {
Rc::new(ByRc {})
}
// methods with an Rc<Self> receiver type can be called
pub fn by_rc(self: Rc<Self>) {}
// Rc<Self> derefs to &self, so this also works
pub fn by_ref(&self) {}
}§Allowed values:
#[methods(self_type = "Owned")](default)#[methods(self_type = "Rc")]#[methods(self_type = "Arc")]#[methods(self_type = "Box")]
Note that methods with a self: Pin<...> receiver are
mockable. self_type does not specify what types of receivers can
be mocked, but how faux stores the instances internally.
§Panics
§Non-stubbed methods
#[faux::create]
pub struct MyStruct {}
#[faux::methods]
impl MyStruct {
pub fn get(&self) -> usize {
50
}
}
let fake = MyStruct::faux();
// fake.get is not stubbed
fake.get(); // <~ panics§Mocking real instances
Spies (real instances with mocked methods) are not supported.
#[faux::create]
pub struct MyStruct {}
#[faux::methods]
impl MyStruct {
pub fn new() -> MyStruct {
MyStruct {}
}
pub fn get(&self) -> usize {
50
}
}
let mut fake = MyStruct::new();
faux::when!(fake.get); // <~ panics§Unsupported receiver/self_type combinations
If a mockable method uses self: Rc<Self> as a receiver and the
self_type is not Rc, faux performs a uniqueness check to
prevent unsound behavior. This also applies to self: Arc<Self>.
In the case of a panic, the message faux produces guides you
towards using the self_type argument in the faux attributes.
use std::rc::Rc;
#[faux::create]
pub struct Owned { /* fields */ }
#[faux::methods]
impl Owned {
pub fn new() -> Owned {
/* implementation */
}
pub fn by_rc(self: Rc<Self>) {
/* implementation */
}
}
let rcd = Rc::new(Owned::new());
// this works because this is the only Rc for the instance
rcd.by_rc();
let rcd = Rc::new(Owned::new());
// clone the Rc so the uniqueness check fails
let clone = rcd.clone();
rcd.by_rc(); // <~ panics: reference is not unique§Known Limitations
- #14: Methods cannot have arguments of the same type as their struct.
- #18: Generic methods and
implreturn types are not supported.
§Caveats
§Returning mockable struct
When referring to the mockable struct in the signature (either by
name or by Self) only special cases are allowed. In particular,
it is only allowed in the return position of the signature for the
follow cases:
-
Returning the struct itself (e.g.,
fn new() -> Self) -
Returning the struct wrapped directly in:
Rc,Arc,Box,Result, orOption. ForResult, referring to the struct is only allowed if it’s theOkvariant of the result. (e.g.,fn load() -> Result<Self, Error>)
Any other kind of return type that refers to the mocked struct is
not supported by faux. Please file an issue if you have a use
case that you believe should be common enough for faux to handle
automatically.
A workaround is to place the functions in an untagged impl block
and have them call methods inside the tagged impl.
pub enum Either<X, Y> {
Left(X),
Right(Y),
}
#[faux::create]
pub struct MyStruct {
x: i32
}
#[faux::methods]
impl MyStruct {
fn new() -> Self {
MyStruct { x: 4 }
}
}
// do not tag this one
impl MyStruct {
pub fn make_either() -> Either<Self, String> {
Either::Left(MyStruct::new())
}
}
let x = MyStruct::make_either();
assert!(matches!(x, Either::Left(MyStruct { .. })));§Paths in types
#[methods] can be added to blocks of the form impl path::to::Type as long as the path does not contain the super
or crate keywords. If it does, use the path argument
to explicitly specify the path.