#[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
impl
return 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 theOk
variant 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.