What is the ideomatic way to abstract dependencies for unit testing (Dependency injection)?
⚓ Rust 📅 2025-11-02 👤 surdeus 👁️ 3In my library, I make HTTP requests via reqwest. To test these functions, I currently use mockall to create mock objects that abstract the HTTP call.
#[cfg_attr(test, automock)]
pub(super) trait HttpFetcher {
    /// Send a HTTP POST request with JSON as payload.
    fn http_post_request(
        &self,
        url: &str,
        payload: serde_json::Value,
        options: &FetchOptions,
    ) -> Result<String, NetworkError>;
}
impl HttpFetcher for Client { 
// Implementation for http_post_request
}
In my tests I then create a mock object to abstract that call
let mut mock = MockHttpFetcher::new();
mock.expect_http_post_request().return_const({
    Ok("Sample response data".into())
});
Now in order to use the mock object, I have two functions: A public function that the consumer of the library is calling, which sets up the dependency and then calls a impl function that contains the actual logic.
pub fn query_data(options: &FetchOptions) -> Result<Project, QueryError> {
    let http_client = Client::new();
    query_data_impl(options, &http_client)
}
fn query_data_impl(
    options: &FetchOptions,
    http_client: &impl HttpFetcher,
) -> Result<Project, QueryError> {
    // Setup here
    let response = http_client.http_post_request(/* params */)
   // Processing response here
}
Now I'm wondering if this is indeed idiomatic Rust code or if there is a better way?
3 posts - 3 participants
🏷️ Rust_feed