How to mock dependency or fn in rust?

Hi, I'm new to rust and would like to know how to write unit-tests that need to mock dependencies or function, similar to go/python. Thanks.

For example, I'd like to mock get_from_api to test myfunc logic.
get_from_api could be an external crate fn or part of my code.

fn myfunc() -> bool {
   let payload = get_from_api()
   let result = ...myfunc logic
   result
}
...
#[test]
fn test_myfunc() {
   assert!(true, myfunc())
}
1 Like

The typical way to mock something during testing purposes is to use some sort of "seam" where you can use some test-specific code that behaves the way you want.

For example, instead of creating a HTTP client inside get_from_api(), you might use traits to pass in some object that knows how to fetch a value, and in production it makes HTTP requests while testing just returns a hard-coded value.

trait CustomerDatabase {
  fn get_all_customers(&self) -> Result<Vec<Customer>, Error>;
}

fn my_func(db: impl CustomerDatabase) -> bool {
  let all_customers = db.get_all_customers();
  let result = ...;
  result
}

I know the example is a bit contrived, but it's normally better to refactor code like that so it's more testable. In my experience, mocks and monkeypatching are often used as workarounds for code that is highly coupled or not structured very well.

In this case, rather than fetching the result from the API inside myfunc(), I would take the result you get from the API and pass it to myfunc() as a parameter. That makes the code trivial to test - no mocking required.

fn myfunc(customers: Vec<Customer>) -> bool {
  ...
}

#[test]
fn test_myfunc() {
  let customers = vec![
    Customer::new("Moe"), 
    Customer::new("Larry"), 
    Customer::new("Curly"),
  ];

  let result = myfunc(customers);
  
  assert_eq!(result, true);
}

As a benefit, it also decouples your business logic from the IO, which makes it usable in more places and easier to maintain.

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.