Attribute Macro faux::methods [−][src]
#[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-mocked methods
#[faux::create] pub struct MyStruct {} #[faux::methods] impl MyStruct { pub fn get(&self) -> usize { 50 } } let fake = MyStruct::faux(); // fake.get is not mocked 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
- #13: Within a module, for a single struct, only a single inherent
impl
and a single traitimpl
per trait may exist. - #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
Returning the mockable struct wrapped as a generic of another type
(e.g., Option<Self>
) is not currently supported. The exception
to this is returning an instance wrapped by the
self_type.
#[faux::create] pub struct MyStruct {} #[faux::methods] impl MyStruct { pub fn try_to_new() -> Result<Self, String> { Ok(MyStruct {}) } }
A workaround is to place these functions in an untagged impl
block and have them call methods inside the tagged impl
.
#[faux::create] pub struct MyStruct {} #[faux::methods] impl MyStruct { fn new() -> Self { MyStruct {} } } // do not tag this one impl MyStruct { pub fn try_to_new() -> Result<Self, String> { Ok(MyStruct::new()) } } let x = MyStruct::try_to_new(); assert!(x.is_ok());
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.