Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compatibility with trait-variant for automock #639

Open
lasantosr opened this issue Feb 11, 2025 · 3 comments · May be fixed by #640
Open

Compatibility with trait-variant for automock #639

lasantosr opened this issue Feb 11, 2025 · 3 comments · May be fixed by #640
Labels
enhancement New feature or request

Comments

@lasantosr
Copy link

This crate automock already has direct support for async_trait (#183), allowing you to place the async_trait macro after the automock (doc) which allows you to directly return the future output when mocking:

#[automock]
#[async_trait]
pub trait Foo {
    async fn foo(&self) -> i32;
}

#[tokio::test]
async fn test() {
    let mut mock = MockFoo::new();
    mock.expect_foo().return_const(1);
    assert_eq!(1, mock.foo().await);
}

If you place the async_trait to be resolved before, you have to return the pinned box future on the mocks, which allows for further customization:

#[async_trait]
#[automock]
pub trait Foo {
    async fn foo(&self) -> i32;
}

#[tokio::test]
async fn test() {
    let mut mock = MockFoo::new();
    mock.expect_foo().returning(|| Box::pin(async { 1 }));
    assert_eq!(1, mock.foo().await);
}

That's great! And I was hoping now that trait-variant is available as part of Rust 1.75 stabilization of async fn in traits that it could work the same way.

This is working:

#[trait_variant::make(Send)]
#[automock]
pub trait Foo {
    async fn foo(&self) -> i32;
}

#[tokio::test]
async fn test() {
    let mut mock = MockFoo::new();
    mock.expect_foo().returning(|| Box::pin(async { 1 }));
    assert_eq!(1, mock.foo().await);
}

But this is failing:

#[automock]
#[trait_variant::make(Send)]
pub trait Foo {
    async fn foo(&self) -> i32;
}

#[tokio::test]
async fn test() {
    let mut mock = MockFoo::new();
    mock.expect_foo().return_const(1);
    assert_eq!(1, mock.foo().await);
}

If the trait variant annotation is removed it works as expected, and if the annotation is added back to the expanded trait it works fine also, so I guess this is not a big change, but I'm not familiar with the codebase.

It would be great to support it the same way in order to allow for an easy migration from async_trait to trait_variant, without having to fix all of the tests.

@asomers asomers added the enhancement New feature or request label Feb 11, 2025
@asomers
Copy link
Owner

asomers commented Feb 11, 2025

This would certainly be a handy feature to have. Basically, we need trait_variant to expand before automock. In your third example, do Foo and MockFoo get :Send bounds? If both of them do, then I don't think there's anything else we need to do.

@lasantosr
Copy link
Author

@asomers The last example does not compile:

error: expected `trait`
 --> src/main.rs:1:1
  |
1 | #[automock]
  | ^^^^^^^^^^^
  |
  = note: this error originates in the attribute macro `automock` (in Nightly builds, run with -Z macro-backtrace for more info)

error: expected non-macro attribute, found attribute macro `trait_variant::make`
 --> src/main.rs:2:3
  |
2 | #[trait_variant::make(Send)]
  |   ^^^^^^^^^^^^^^^^^^^ not a non-macro attribute

error[E0658]: custom attributes cannot be applied to statements
 --> src/main.rs:2:1
  |
2 | #[trait_variant::make(Send)]
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: see issue #54727 <https://github.com/rust-lang/rust/issues/54727> for more information

@lasantosr lasantosr linked a pull request Feb 11, 2025 that will close this issue
@lasantosr
Copy link
Author

lasantosr commented Feb 11, 2025

@asomers Sorry I misunderstood your previous question, the third example does indeed compile, and Foo trait gets Send bounds, with MockFoo properly implementing it.

The problem is that the fourth example (the last one) doesn't compile, where the trait_variant macro expands after automock (same way as with async_trait on the first example, which compiles just fine).

That way we don't have to Box::pin the expectations and can just use the future output, in order to migrate from async_trait to trait_variant without having to rewrite tests.

I've made a PR where the trait_variant::make attribute is just forwarded to the original trait definition, and nowhere else, for the last example to also compile.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants