[][src]Attribute Macro faux::methods

#[methods]

Transforms the given methods into mockable versions of themselves and provides a new method to mock them.

The generated methods look like

This example is not tested
impl MyStruct {
    /* other methods before */

    // I is a tuple of all the non-receiver arguments of #{method_name}
    // O is the output of #{method_name}
    _when_#{method_name}(&mut self) -> When<I,O> {
        /* auto generated code */
    }
}

These auto-generated methods can be called directly but a more ergonomic way is by 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

There are times when faux needs a little extra information to properly mock your struct and its methods. Use path = "path::to::imported::mod" when you are mocking methods from a struct imported from a different module. Use self_type = "{self_type}" when your method receivers would not allow faux to get an owned value from it, this is particularly important for self: Rc<Self> and self: Arc<Self> receivers.

path

Faux supports mocking structs from a different module as long as we tell #[methods] where we are importing the struct from.

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

The self_type specified in methods must match the self_type in create and is required iff one was specified there.

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 now 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 still return plain Self
    pub fn new() -> Self {
        ByRc {}
    }

    // but you may also now return 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
    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();
// when!(fake.get).then_safe() was not invoked and thus the method was not mocked
fake.get(); // <~ panics with "'MyStruct::get' is not mocked"

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 with "not allowed to mock a real instance!"

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(); // this panics because there are now multiple references

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.

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.

This example deliberately fails to compile
#[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.