[][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.

Known Limitations

#10: impl SomeTrait for SomeStruct {} is not supported.

#13: Only a simple impl block may exist per module per type.

#14: Methods may not contain instances of the same struct as parameters.

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
    }
}

// #[methods]
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);

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!"

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 mocked.

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());

Mocking struct defined elsewhere in the crate

Faux supports mocking structs from a different module as long as we tell #[methods] where we are importing the struct from using the #[methods(path::to::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 {
        use super::MyStruct;

        // we have to tell #[faux::methods] where we imported MyStruct from
        #[faux::methods(super)]
        impl MyStruct {
            pub fn four(&self) -> i32 {
                self.three() + 1
            }
        }
    }
}

mod bar {
    use crate::foo::MyStruct;

    // we have to tell #[faux::methods] where we imported MyStruct from
    #[faux::methods(crate::foo)]
    impl 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);