asomers / mockall

A powerful mock object library for Rust
Apache License 2.0
1.5k stars 62 forks source link

Cannot refer to associated types in a `mock!` in async test function #432

Closed Pet3ris closed 1 year ago

Pet3ris commented 1 year ago

The following code does not work:

trait Zen {
    type Item;
    fn zen() -> Self::Item;
}
mock! {
    pub Z {}
    impl Zen for Z {
        type Item = u64;
        fn zen() -> Self::Item;
    }
}

Detailed error

error[E0223]: ambiguous associated type
   --> src/commands/run.rs:313:17
    |
313 |               pub Z {}
    |  _________________^
314 | |             impl Zen for Z {
315 | |                 type Item = u64;
316 | |                 fn zen() -> Self::Item;
    | |______________________________________^ help: use fully-qualified syntax: `<MockZ as Trait>::Item

But the following code does:

trait Zen {
    type Item;
    fn zen() -> Self::Item;
}
mock! {
    pub Z {}
    impl Zen for Z {
        type Item = u64;
        fn zen() -> u64;
    }
}

While using an external create, this adds some extra work in copy/pasting associated type instantiations.

Tested under mockall = "0.11.2".

Note, this version, similar to what is specified in mock! documentation also didn't work:

mock! {
    pub Z {}
    impl Zen for Z {
        type Item = u64;
        fn zen() -> <Self as Zen>::Item;
    }
}

Detailed error

error[E0433]: failed to resolve: use of undeclared type `Zen`
   --> src/commands/run.rs:316:38
    |
316 |                 fn zen() -> <Self as Zen>::Item;
    |                                      ^^^ use of undeclared type `Zen`
mock! {
    pub Z {}
    impl Zen for Z {
        type Item = u64;
        fn zen() -> <Self as Z>::Item;
    }
}

Detailed error

error[E0282]: type annotations needed
   --> src/commands/run.rs:312:9
    |
312 | /         mock! {
313 | |             pub Z {}
314 | |             impl Zen for Z {
315 | |                 type Item = u64;
316 | |                 fn zen() -> <Self as Z>::Item;
317 | |             }
318 | |         }
    | |_________^ cannot infer type
asomers commented 1 year ago

The necessity to inline the associated type into the mock method signature is a limitation that can't be robustly fixed, at least not without introducing new syntax. But the QSelf syntax does work. In your case, the use of undeclared type Zen error is probably due to some kind of module problem: you simply haven't imported the Zen name into the right place. Your exact code works for me. If you still can't figure it out, please post a complete example file.

Pet3ris commented 1 year ago
#[cfg(test)]
mod tests {
    use async_trait::async_trait;
    use mockall::*;

    use super::*;

    #[tokio::test]
    async fn test_mockall() {
        trait Zen {
            type Item;
            fn zen() -> Self::Item;
        }
        mock! {
            pub Z {}
            impl Zen for Z {
                type Item = u64;
                fn zen() -> <Self as Zen>::Item;
            }
        }
    }
}
Pet3ris commented 1 year ago

This works:

#[cfg(test)]
mod tests {
    use async_trait::async_trait;
    use mockall::*;

    use super::*;

    trait Zen {
        type Item;
        fn zen() -> Self::Item;
    }
    mock! {
        pub Z {}
        impl Zen for Z {
            type Item = u64;
            fn zen() -> <Self as Zen>::Item;
        }
    }

    #[tokio::test]
    async fn test_mockall() {
        let mock = MockZ::new();
    }
}