What is the ideomatic way to abstract dependencies for unit testing (Dependency injection)?
⚓ Rust 📅 2025-11-02 👤 surdeus 👁️ 12In 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