Skip to content

Allow expectation on async trait function to return a future #189

Open
@e00E

Description

@e00E

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions