Description
I saw in the changelog that you implemented support for async_trait. Thank you!
I noticed a limitation in this implementation. In the expectation we have to return a non future value. This prevents some advanced mocking from working:
#[mockall::automock]
#[async_trait::async_trait]
trait T {
async fn foo(&self) -> u32;
}
fn main() {
let mut t = MockT::new();
t.expect_foo().returning(|| 1);
// These two don't work:
t.expect_foo().returning(|| async { 1 });
t.expect_foo()
.returning(|| async { futures::future::pending().await });
}
This is an observable difference because async functions can either return their value or be pending. By only being able to return the values we cannot mock and test pending behavior. In the async { 1 }
you can imagine that we want to be pending first for a short time (like sleeping, or waiting for a signal) and only then return the value. Or we get the value from an async channel.
These types of tests are probably rare and the current functionality is correct for most users but it might be nice to have. From an api perspective the most general solution is to have expect_foo
always return a future so you would need to start an async block in the closure. I understand this is a little more work for users that don't need this but it is more correct.
Alternatively there could be another postfix like returning_async
but I'm not sure how this meshes with the existing _st
.
Or maybe mockall could detect whether returning
on an async trait function expectation returns a normal value or a future. I don't know whether this is possible.