Looking for some tips on how to write a code that is easy to unit test

Hi there. I'm new to Rust and it is the first time I'm posting here. :slight_smile:

In order to learn more about it I have decide to write a small API to save/provide some data to the consumer. I'm now using PostgreSQL and Redis as data repositories and actix as http framework.

The layers I have are organized into: controller > service > repository

The controller knows everything about http requests and it communicate with the service using a common API.

The service does not know anything about http or about the repository but it recieves messages from the controller, apply some rules and communicates with the repo layer using a common api.

The repo does not know anything about the service or about the controller. It only knows how to communicate with the database (at the current moment: PostgreSQL or Redis).

They all have common structures defined so they can communicate to each other.

Having that in mind. I would like to hide some crate types behind my own types.

So my struggle is: Let's say I'm on the service layer and I've a variable of a struct that implements PgRepository trait. I would like them to extract a connection from that trait and use it as a parameter to communicate with the PostgreSQL repository layer. The repo layer is expecting a Connection type that is later translated to PgConnection.

Here I have some code:

use ::r2d2::PooledConnection;
use diesel::r2d2::{self, ConnectionManager};
use diesel::PgConnection;
use r2d2_redis::RedisConnectionManager;

type PgPool = ConnectionManager<PgConnection>;
type RedisPool = RedisConnectionManager;

pub trait PgRepository: Default + Send + Sync + 'static
{
    type Connection: diesel::Connection;
    fn get_connection(&self) -> Result<Self::Connection, ErrorWrapper>;
}

pub trait RedisRepository: Default + Send + Sync + 'static
{
    type Connection;
    fn get_connection(&self) -> Result<Self::Connection, ErrorWrapper>;
}

/* ----------------------------------------------------------------------------------------------------- */

pub struct PgRepositoryPool {
    pool: r2d2::Pool<PgPool>,
}

impl Default for PgRepositoryPool {
    fn default() -> Self {
        info!("Initializing Database Pool");

        let db_config = RainbowConfigDb::new();

        let postgres_manager = ConnectionManager::<PgConnection>::new(db_config.get_database_url());
        let postgres_pool: r2d2::Pool<PgPool> = r2d2::Pool::builder()
            .build(postgres_manager)
            .expect("Failed to create Postgres pool");

        info!("Running Database Migrations");
        embed_migrations!();
        let conn = postgres_pool.get().unwrap();
        embedded_migrations::run(&*conn).unwrap();

        PgRepositoryPool {
            pool: postgres_pool,
        }
    }
}

impl PgRepository for PgRepositoryPool {
    type Connection = PooledConnection<PgPool>;

    fn get_connection(&self) -> Result<Self::Connection, ErrorWrapper> {
        let conn = self.pool.get()?;
        Ok(conn)
    }
}


/* ----------------------------------------------------------------------------------------------------- */

pub struct RedisRepositoryPool {
    pool: r2d2::Pool<RedisConnectionManager>,
}

impl Default for RedisRepositoryPool {
    fn default() -> Self {
        info!("Initializing Redis Pool");

        let redis_config = RainbowConfigRedis::new();

        let redis_manager = RedisConnectionManager::new(redis_config.get_redis_url()).unwrap();
        let redis_pool: r2d2::Pool<RedisPool> = r2d2::Pool::builder()
            .build(redis_manager)
            .expect("Fail to create Redis Pool");

        RedisRepositoryPool { pool: redis_pool }
    }
}

impl RedisRepository for RedisRepositoryPool {
    type Connection = PooledConnection<RedisPool>;

    fn get_connection(&self) -> Result<Self::Connection, ErrorWrapper> {
        let conn = self.pool.get()?;
        Ok(conn)
    }
}

Having that code when I try to use the get_connection I receive a Trait::Connection which is not the same as PooledConnection so the compiler complains:

mismatched types

expected struct `diesel::PgConnection`, found associated type

note: expected reference `&diesel::PgConnection`
         found reference `&<D as db::repository::PgRepository>::Connection`

I'm trying to write such code in way I can have those layers more independent from each other and to have an easy way to mock data later for the tests. So for the tests I want to write a new structure that implements the Repository Trait and has a type I can implement.

I'm not sure if that is the best or correct approach/way of thinking about the problem so I would like some help. Any tips, hints, better approaches are ultra welcome :smiley:

Later I want to learn more about the mockall crate so I can use to test the service layer (unit tests). The idea is to mock the functions I call from the repository layer so I can return the data I need to force the test to follow the path I want it to follow in each unit test.

I know the post is a bit long and I'm sorry for that but I would like to thanks everyone for reading and supporting those who are starting with this cool language :slight_smile:

Best regards!!!