[−][src]Attribute Macro faux::methods
#[methods]
Transforms methods in an impl
block into mockable versions of
themselves.
The mockable methods can then be mocked using when!.
Associated functions and private methods are not mocked, and are instead proxied to the real implementation.
Requirements
#[create] must have been previously called for this struct.
Usage
#[faux::create] pub struct MyStruct { /* fields */ } #[faux::methods] impl MyStruct { pub fn new(data: Vec<u32>) -> Self { /* implementation code */ } pub fn get(&self) -> usize { 20 } } use std::io::{self, Read}; #[faux::methods] impl Read for MyStruct { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { /* potentially complicated 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 when mocking their methods let mut fake = MyStruct::faux(); faux::when!(fake.get).safe_then(|_| 3); assert_eq!(fake.get(), 3); // unsafe because a parameter is a reference. // See When's documentation unsafe { 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.
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 from somewhere else use super::MyStruct; // so we have to tell faux where it came from #[faux::methods(path = "super")] impl MyStruct { pub fn four(&self) -> i32 { self.three() + 1 } } } } mod bar { // we are importing a module from somewhere else use crate::foo; // so we need to tell faux where that module came from #[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).safe_then(|_| 30); faux::when!(x.four).safe_then(|_| 40); faux::when!(x.five).safe_then(|_| 50); assert_eq!(x.three(), 30); assert_eq!(x.four(), 40); assert_eq!(x.five(), 50);
self_type
Allowed values:
#[methods(self_type = "Rc")]
#[methods(self_type = "Arc")]
#[methods(self_type = "Box")]
#[methods(self_type = "Owned")]
- this is the default and not necessary
Indicates how the real value of the struct is wrapped when not
being mocked, e.g., wrapped as an owned instance vs an
Rc<>
. faux
will guide to use this attribute when needed
through either a compile time error or a panic. Do not use unless
faux
asks you to.
If this attribute is set, the #[create] attribute must specify
the same self_type
in the struct.
By default faux
assumes that it has access to an owned instance
of the struct. However, the #[create] macro may have a
self_type
specified that wraps the instance differently. This is
useful when the method receivers are all the same non-owned
received (e.g., self: Rc<Self>
).
The method receivers for all the methods in the impl block must be
convertable from the self_type
specified. In particular, while a
&self
can be obtained from an Rc<Self>
or an Arc<Self>
, a
&mut self
cannot. This means that if you specify self_type = "Rc"
, then none of the methods being mocked may take a &mut self
as a receiver. If you believe that a certain combination of
specified self_type
and method receiver is doable but not
allowed in faux
please file an issue.
Another effect of specifying the self_type
is gaining the
ability to include methods and associated functions that return
Self
wrapped in that pointer type.
use std::rc::Rc; #[faux::create(self_type = "Rc")] pub struct ByRc {} #[faux::methods(self_type = "Rc")] impl ByRc { // you can return plain Self pub fn new() -> Self { ByRc {} } // or the Self wrapped in the self_type pub fn new_rc() -> Rc<Self> { Rc::new(ByRc {}) } // can call methods with an Rc<Self> receiver type pub fn by_rc(self: Rc<Self>) {} // Rc<Self> derefs to &self so this is okay too pub fn by_ref(&self) {} }
Panics
Non-mocked methods
Faux will not try to return a default value on non-mocked methods so it panics instead.
#[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 are not supported and thus mocking real instances panic.
#[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
Structs with self: Rc<Self>
or self: Arc<Self>
methods that have been cloned
While you do not need to specify #[self_type = "Rc"]
or
#[self_type = "Arc"]
even if you have self: Rc<Self>
or self: Arc<Self>
receivers respectively, the real instances that are
created through this must be the only reference to the object
when calling these methods or else your test will fail.
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 */ } } // works if there is only a single reference let rcd = Rc::new(Owned::new()); rcd.by_rc(); // this works // panics if there are multiple references let rcd = Rc::new(Owned::new()); let clone = rcd.clone(); rcd.by_rc(); // <~ panics: reference is not unique
In the case of a panic the panic message faux produces should
guide you towards using the self_type
argument in the faux
attributes
Known Limitations
#13: Only a single inherent impl block and a single trait implementation per trait per type may exist.
#14: Methods may not contain instances of the same struct as parameters.
#18: Generic methods and impl
return types are not allowed
Caveats
Methods/functions that return the mocked struct
Special care is taken for methods and function that return an
instance of the mocked struct. Unfortunately only methods that
return -> Self
or -> #{SomeStruct}
are
handled.
Methods/functions that returns your type wrapped as a generic of
another type (e.g., Result<Self, _>
) cannot be wrapped in a faux
impl. The exception to this is methods that receive an specified
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 outside the impl tagged
with #[faux::method]
and have it redirect to the method 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
faux
supports implementing types of the form path::to::Type
as
long as the path does not contain any super
or crate
keywords. To implement such types use the path
argument.