I’m developing a Rust project using MongoDB. While writing unit tests, I encountered the #[non_exhaustive] attribute used in the MongoDB library.
As I’m mocking UserRepository(mockall) which implements MongoDB functions, I’m unable to mock the result by directly passing UpdateResult because I can’t instantiate it.
How can I work around this issue with UpdateResult?
I understand that non_exhaustive serves precisely to prevent you from directly instantiating a struct, but this is a unit test, I need to mock the result of an external lib.
Are aware of this challenge when working with unit testing, but will not implement it for now
https://jira.mongodb.org/browse/RUST-1891
Here is a simplified snippet of the code:
test.rs
#[cfg(test)]
mod tests {
use mongodb::results::UpdateResult;
use crate::{
modules::user::{repository::MockUserRepository, service::UserService},
tests::unit::common::get_user_to_test,
};
#[tokio::test]
async fn check_if_set_session_id_in_user() {
let mut mock_repo = MockUserRepository::new();
let mock_update_result = UpdateResult { //cannot create non-exhaustive struct using struct expression
matched_count: 1,
modified_count: 1,
upserted_id: None,
};
mock_repo
.expect_update_one()
.returning(move |_, _, _| Ok(mock_update_result));
let user_service = UserService::new(mock_repo);
let mut user = get_user_to_test();
user.session_id = None;
assert!(user.session_id.is_none());
let result = user_service.check_or_add_session(&mut user).await;
assert!(result.is_ok());
assert!(user.session_id.is_some());
}
}
repository.rs
#[automock]
#[async_trait]
pub trait UserRepository {
async fn update_one(
&self,
query: Document,
update: Document,
options: Option<UpdateOptions>,
) -> Result<UpdateResult, Error>;
async fn find_one(
&self,
filter: Document,
options: Option<FindOneOptions>,
) -> Result<Option<User>, Error>;
}
#[derive(Clone)]
pub struct MongoUserRepository {
collection: Collection<User>,
}
impl MongoUserRepository {
pub fn new(collection: Collection<User>) -> Self {
Self { collection }
}
}
#[async_trait]
impl UserRepository for MongoUserRepository {
async fn update_one(
&self,
query: Document,
update: Document,
options: Option<UpdateOptions>,
) -> Result<UpdateResult, Error> {
self.collection.update_one(query, update, options).await
}
async fn find_one(
&self,
filter: Document,
options: Option<FindOneOptions>,
) -> Result<Option<User>, Error> {
self.collection.find_one(filter, options).await
}
}
3
[non_exhaustive] prevents you from creating an instance of the struct by usual methods and instead you should create the instance provided by the update_one() and update_many() functions
In the case of testing you should create your own mock struct :
#[derive(Debug, Clone)]
struct MockUpdateResult {
pub matched_count: u64,
pub modified_count: u64,
pub upserted_id: Option<bson::Bson>,
}
impl From<MockUpdateResult> for UpdateResult {
fn from(mock: MockUpdateResult) -> Self {
UpdateResult {
matched_count: mock.matched_count,
modified_count: mock.modified_count,
upserted_id: mock.upserted_id,
}
}
}
then implement this custom struct
Cosmin Drăguşanu is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
1