Context & my attempts
I am trying to unit-test a function, and I want to make sure it calls the correct API endpoint with the correct arguments.
My first idea was to make the function I wanted to test accept the API as a trait object. This would allow me to mock it. I also had to pass mutable references, because the mock should record information about how it was called. Below you see a simplified representation of my code structure:
trait API {
fn func_a(&mut self, arg1: i32, arg2: String);
}
// prod trait implementation
#[allow(dead_code)]
fn business_logic(api: &mut dyn API) {
api.func_a(42, String::from("mytrousersareblue"));
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Default)]
struct Mock<A, B> {
args: (A, B),
}
impl API for Mock<i32, String> {
fn func_a(&mut self, arg1: i32, arg2: String) {
self.args = (arg1, arg2);
}
}
#[test]
fn business_logic_performs_correct_calls() {
let mut mock: Mock<i32, String> = Default::default();
business_logic(&mut mock);
assert_eq!(mock.args.0, 42);
assert_eq!(mock.args.1, String::from("mytrousersareblue"));
}
}
This has various downsides, mainly that I have to make separate Mock
structs for functions with zero, one, two, three, and more parameters.
So another idea that came to my mind was to implement the Mock
struct as follows:
struct Mock<'a> {
args: Vec<&'a dyn Eq>,
}
This would allow me to record as many arguments as I want. However the compiler complains that the trait std::cmp::Eq
cannot be made into an object.
I cannot use std::any::Any
, because I still need to be able to determine equality in the assert_eq
statements.
Maybe I am taking a completely wrong path here. I do have quite some trouble with the Rust mindset, coming from a Java background...
Summary / TLDR
I want to have a mock-implementation of the API, which records all arguments it was called with, that can later be compared against expected values.
If I'm correct, then I'll need a way to make a struct store any number of objects, which all extend at least Eq
. However, if there's a nicer solution: I'm open.