Home > other >  mock trait implementation for concrete struct
mock trait implementation for concrete struct

Time:11-02

So I have a function. And I want to test it. It takes a struct as param. And the struct has some trait implemented. This trait has a long running io method. I don't want this io method to actually go fetch data, I want to mock this io method and just return the result. I am a little lost about how this can be done. Here is my try (not working)

struct TestStruct {
    a: u32,
    b: u32,
}
pub trait TestTrait {
    fn some_long_running_io_method(&self) -> u32 {
        156
    }
}
fn important_func(a: TestStruct) {
    println!("a: {}", a.some_long_running_io_method());
}
impl TestTrait for TestStruct {
    fn some_long_running_io_method(&self) -> u32 {
        self.a   self.b
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    use mockall::predicate::*;
    use mockall::*;
    #[cfg(test)]
    mock! {
        pub TestStruct {}
        impl TestTrait for TestStruct {
            fn some_long_running_io_method(&self) -> u32;
        }
    }
    #[test]
    fn test_important_func() {
        let mut mock = MockTestStruct::new();
        mock.expect_some_long_running_io_method()
            .returning(|| 1);
        important_func(mock);
    }
}

I obviously get this error:

error[E0308]: mismatched types
  --> src/test.rs:35:24
   |
35 |         important_func(mock);
   |         -------------- ^^^^ expected struct `TestStruct`, found struct `MockTestStruct`
   |         |
   |         arguments to this function are incorrect

How can I achieve mocking trait methods? 1) One way is to change function param and instead of accepting a concrete struct accept trait. And implement this trait on MockTestStruct. But then we have dynamic dispatching and it hurts the performance. I don't want a performance degrade just for the test. 2) I also tried reimplementing the Trait right where the test is, but conflicting implementations are not allowed in Rust. 3) Make function accept TestStruct or MockTestStruct? Probably not great way either.

Could you please tell me what is the idiomatic way to do it?

CodePudding user response:

You can make your function, important_func a generic function. You can then use generic bounds to restrict the type to implementors of your trait.

Here is an example with your code:

struct TestStruct {
    a: u32,
    b: u32,
}
pub trait TestTrait {
    fn some_long_running_io_method(&self) -> u32 {
        156
    }
}

// important_func can now use any type T which implements TestTrait,
// including your mock implementation! 
fn important_func<T: TestTrait>(a: T) {
    println!("a: {}", a.some_long_running_io_method());
}
impl TestTrait for TestStruct {
    fn some_long_running_io_method(&self) -> u32 {
        self.a   self.b
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    use mockall::predicate::*;
    use mockall::*;
    #[cfg(test)]
    mock! {
        pub TestStruct {}
        impl TestTrait for TestStruct {
            fn some_long_running_io_method(&self) -> u32;
        }
    }
    #[test]
    fn test_important_func() {
        let mut mock = MockTestStruct::new();
        mock.expect_some_long_running_io_method().returning(|| 1);
        important_func(mock);
    }
}

  • Related