Skip to content

should DatabaseConnectionType::MockDatabaseConnection use Arc<dyn MockDatabaseTrait> #3013

@FreeMasen

Description

@FreeMasen

Motivation

Currently, mocking only allows for explicitly defining list of events ahead of time. While this provides enough utility to get some basic testing accomplished, it limits the ability to assert the state of the database after a test has completed.

For example

struct SomeDbWrapper(DatabaseConnection);


impl SomeDbWrapper {
    pub fn new(conn: DatabaseConnection) -> Self {
        Self(conn)
    }

    pub async fn do_something_complicated(&self, other_data: Vec<(u32, u32, u32)>) -> Result<(), DbErr> {
        let tx = self.0.begin().await?;
        // This table has a composite key, that represents the only potential conflict
        // if that is violated, we want to update the data column
        let mut conflict_policy = OnConflict::new();
        conflict_policy.update_columns(
            Column::Data
        );
        // we want to clean up any data that isn't in the `other_data` provided so we will collect
        // a filter here of the composite keys
        let mut missing_items_filter = None;
        for (ida, idb, data) in other_data {
            if let Some(expr) = missing_items_filter {
                missing_items_filter = Some(expr.or(
                    Column::IdA.eq(ida).and(
                        Column::IdB.eq(idb)
                    ).not()
                ))
            } else {
                missing_items_filter = Some(
                    Column::IdA.eq(ida).and(
                        Column::IdB.eq(idb)
                    ).not()
                );
            }
            Insert(ActiveModel {
                ida: Set(ida),
                idb: Set(idb),
                data: Set(data),
            })
            .on_conflict(on_conflict.clone())
            .exec(&tx).await?;
        }
        let del = Entity::delete_many();

        if let Some(expr) = missing_items_filter {
            del = del.filter(expr);
        }
        del.exec(&tx).await?;
        
        tx.commit().await?;
        Ok(())
    }
}

The above would be very difficult to test with the current MockConnection since we cannot assert which operations have occurred, only what sql statements were executed. If we wanted to test that stale data is cleaned up after calling do_something_complicated we would need to attach to an actual database which can be a cumbersome process.

Proposed Solutions

While the work to build a fully functional in memory database from for testing is likely not something that is in the scope of this project, there is an escape hatch already provided and that is the MockDatabaseTrait,
by updating DatabaseConnectionType::MockDatabaseConnection(Arc<crate::MockDatabaseConnection>) to DatabaseConnectionType::MockDatabaseConnection(Arc<dyn MockDatabaseTrait>) (potentially with + Send + Sync + 'static in any combination) it would allow users to provide their own implementations w/o the maintenance burden.

Additional Information

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions