nrxus / faux

Struct mocking library for Rust
https://nrxus.github.io/faux/
MIT License
411 stars 14 forks source link

Can't use #[faux::methods] on an impl Clone for X {} block #40

Closed coolreader18 closed 3 years ago

coolreader18 commented 3 years ago
struct Foo;
#[faux::methods]
impl Clone for Foo {
    fn clone(&self) -> Self {
        Foo
    }
}
error[E0308]: mismatched types
 --> src/lib.rs:3:1
  |
3 | #[faux::methods]
  | ^^^^^^^^^^^^^^^^ expected struct `_FauxOriginal_Foo`, found struct `Foo`
  |

This happens whether or not I manually change Self -> Foo. Looking at the expanded source, I think it's because the methods macro unconditionally wraps the generated match in MaybeFaux::Real, even though it should wrap the result of call_mock in MaybeFaux::Faux

nrxus commented 3 years ago

Is the code snippet above complete? It looks like you may be missing the #[faux::create] on the Foo struct.

nrxus commented 3 years ago

Ah! Nevermind, even when adding the #[faux::create] it fails. I will definitely take a look soon, although I am open to PRs if you beat me to it!

coolreader18 commented 3 years ago

I think I've got something that should work, hopefully!

nrxus commented 3 years ago

Fixed in dd2e648fbdf7b373719a09cd94f9bcdbd7a10c08 I have released a new version with this fix.

There is one gotcha, a test like this one wouldn't work.

let mock = Foo::faux();
// then_return uses `clone()` to clone a new instance on every mock invocation
// but the returned `Foo::faux()` doesn't have clone mocked.
faux::when!(mock.clone()).then_return(Foo::faux());

let cloned = mock.clone();  // <~~ test panics here with an error "Foo::clone" is not mocked. 

The error message is correct but confusing. The Foo::faux() that we said to return on every mock does not implement clone, so when the mock is invoked and tries to clone the returned Foo::faux() it fails because it was never mocked.

To get around this you can do:

let mock = Foo::faux();
// then doesn't use `clone()`, it just calls the mock implementation
faux::when!(mock.clone()).then_return(|_| Foo::faux());

let cloned = mock.clone();  // works

Using .once().then_return(/* */) would also work as .once() makes it no longer need to clone.

Let me know if something didn't make sense so I can clarify, it took me a while to wrap my head around it when my test wasn't working initially.