mockall icon indicating copy to clipboard operation
mockall copied to clipboard

Support for return_const Option<&T> and Result<&T, E>

Open neilisaac opened this issue 2 years ago • 7 comments

Mocking a trait method fn foo(&self) -> Option<&T> is difficult because return_const wants Option<&T>, so the mock doesn't own the value. It would be more useful if return_const would accept Option<T> (similar to a mocked method returning &T) instead so that the expectation can own the T and return a reference to it. I had to use lazy_static to work around this.

neilisaac avatar Jun 13 '22 15:06 neilisaac

That would be handy, but there are infinitely many possible structs that might be parameterized on &T. Can you think of a more general way to design this feature, preferably one that is possible to implement? I couldn't.

asomers avatar Jun 13 '22 16:06 asomers

I concur - this is a very handy feature to have.

krojew avatar Jan 17 '23 16:01 krojew

@asomers in general supporting something like fn return_ref_from<V: AsRef<T>>(value: V) might allow for a more general solution by internally storing value as a Box<dyn AsRef<T>>. Unfortunately Option implements its own as_ref method rather than implementing AsRef, so I don't think there's an existing trait that can be leveraged directly for Option. Mockall could expose its own trait similar to AsRef, which is implemented for both Option and AsRef. Would that work?

neilisaac avatar Jan 17 '23 20:01 neilisaac

That would only work if you want the mock method to return &T, right? That's already possible using return_ref. So I don't see what this proposal would add.

asomers avatar Feb 09 '23 19:02 asomers

Is there now a way to mock a method returning Option<&T> without a 'static lifetime? Could you give an example @asomers how one would approach this?

gollth avatar Feb 19 '24 12:02 gollth

@gollth there isn't a way to do it automatically. You'll have to do it at least partly by hand. Perhaps something like this:

struct MyStruct<T> {...}
impl<T> MyStruct<T> {
    fn foo(&self) -> Option<&T>;
}
mock!{
    MyStruct<T: 'static> {
        fn _foo(&self) -> Option<T>
    }
}
impl<T: 'static> MockMyStruct<T> {
    fn foo(&self) -> Option<&T> {
        self._foo().as_ref()
    }
}

asomers avatar Feb 19 '24 13:02 asomers

Thanks for the quick answer @asomers. This would probably work. A colleague of mine suggested another approach, by leaking a Box into a static reference:

struct X;  // dummy payload

#[mockall::automock]
trait Foo {
    fn foo(&self) -> Option<&X>;
}

#[test]
fn test() {
    let mut mock = MockFoo();
    let x = Box::leak(Box::new(Some(X)));
    mock.expect_foo().return_const(x.as_ref());
    // ...
}

This works for my use case (non-generic signature). Don't know if this helps for the original question, just thought I post here for reference (=

gollth avatar Feb 19 '24 15:02 gollth