mockall icon indicating copy to clipboard operation
mockall copied to clipboard

Mock the same generic trait twice on a concrete struct

Open glueball opened this issue 4 years ago • 2 comments

First, many thanks for mockall. It's an awesome crate, it has become central to my (and my teams') development experience in Rust.

Not sure if this is possible to do, and I've missed how to do it in the documentation. If a trait has generic parameters, Rust allows a struct (or an enum) to implement multiple times for different parameter objects. How can I mock such an object?

Consider this example:

use mockall::mock;

trait MyTrait<T> {
    fn value(&self) -> T;
}

struct MyStruct {
    integer: i32,
    float: f32,
}

impl MyTrait<i32> for MyStruct {
    fn value(&self) -> i32 {
        self.integer
    }
}

impl MyTrait<f32> for MyStruct {
    fn value(&self) -> f32 {
        self.float
    }
}


mock! {
    MyStruct {}

    impl MyTrait<i32> for MyStruct {
        fn value(&self) -> i32;
    }

    impl MyTrait<f32> for MyStruct {
        fn value(&self) -> f32;
    }
}

It gives the following error:

error[E0592]: duplicate definitions with name 'expect_value'

This example is derived from my use case: a "Context" object that acts as a centralized container/factory for several entities in my project. It implements a trait of the form Builder<E: Entity> for every of such entities. But I'm assuming there will be much less exotic use cases: think, for example, the From<T> standard trait.

Is there some way to mock an object like this? If there is, could you please point me to it? If not, looks like that an (opt-in) way of manually specifying the name of the "expect_" method would be enough:

mock! {
    MyStruct {}

    impl MyTrait<i32> for MyStruct {
       #[mockall(method=expect_value_integer)]
        fn value(&self) -> i32;
    }

    impl MyTrait<f32> for MyStruct {
       #[mockall(method=expect_value_float)]
        fn value(&self) -> f32;
    }
}

glueball avatar Oct 09 '21 18:10 glueball

Note: I'm working around this issue by adding "fake" inherent methods to the mock and then implementing the traits manually

mock! {
    MyStruct {
        fn value_integer(&self) -> i32;
        fn value_float(&self) -> f32;
    }
}

impl MyTrait<i32> for MockMyStruct {
    fn value(&self) -> i32 {
        self.value_integer()
    }
}

impl MyTrait<f32> for MockMyStruct {
    fn value(&self) -> f32 {
        self.value_float()
    }
}

However, it'd be great if there was a way (and less verbose) way of doing this.

glueball avatar Oct 09 '21 18:10 glueball

Currently there's no way to do it if MyStruct is concrete. If MyStruct is generic, however, then it works and requires no special syntax. Ideally Mockall could create a generic MockMyStruct::expect<T> method, where T is implemented for f32 and i32, but Rust doesn't allow such a thing. For now your workaround is the best possible.

asomers avatar Oct 09 '21 18:10 asomers