46 - Construyendo una api rest en Rust - parte 3 implementando tests

46 - Construyendo una api rest en Rust - parte 3 implementando tests

¡Bienvenidos de nuevo a Rustaceo.es! En las entregas anteriores, construimos nuestra API REST en Rust con Actix-Web y agregamos persistencia con SQLx. Ahora, nos enfocaremos en una parte crucial del desarrollo: escribir tests automatizados para asegurar el correcto funcionamiento de nuestra API.


🚀 Objetivos de esta parte #

  1. Escribir tests unitarios y de integración para los endpoints.
  2. Simular peticiones HTTP con actix-web::test.
  3. Validar la correcta interacción con la base de datos.

🛠 Configuración de los tests #

Para probar nuestra API, utilizaremos actix-web::test. Ya tenemos Actix-Web en Cargo.toml, pero asegurémonos de incluir SQLx con un entorno de prueba:

[dev-dependencies]
actix-rt = "2"
sqlx = { version = "0.6", features = ["postgres", "runtime-tokio-native-tls"] }
serde_json = "1"
reqwest = { version = "0.11", features = ["json"] }

🔍 Escribiendo tests unitarios #

Los tests unitarios verifican el comportamiento de funciones individuales sin depender de la base de datos ni de la API completa.

📌 1️⃣ Testeando la función de bienvenida #

#[Cfg(test)]
mod tests {
    use super::*;
    use actix_web::{test, App};

    #[actix_web::test]
    async fn test_bienvenida() {
        let app = test::init_service(App::new().route("/", web::get().to(bienvenida))).await;
        let req = test::TestRequest::get().uri("/").to_request();
        let resp = test::call_service(&app, req).await;
        assert!(resp.status().is_success());
    }
}

test::init_service() inicializa la aplicación para pruebas. ✔ test::TestRequest::get().uri() simula una petición HTTP GET. ✔ assert!(resp.status().is_success()) valida que la respuesta sea exitosa.


🔄 Escribiendo tests de integración #

Los tests de integración verifican cómo interactúan las partes del sistema, incluyendo la base de datos.

📌 2️⃣ Testeando la creación de un pokémon #

#[Actix_web::test]
async fn test_agregar_elemento() {
    let pool = establecer_conexion_test().await;
    let app = test::init_service(
        App::new()
            .app_data(web::Data::new(pool.clone()))
            .route("/elementoes", web::post().to(agregar_elemento)),
    )
    .await;

    let nuevo_elemento = json!({"nombre": "Item3", "tipo": "Agua"});
    let req = test::TestRequest::post()
        .uri("/elementoes")
        .set_json(&nuevo_elemento)
        .to_request();

    let resp = test::call_service(&app, req).await;
    assert_eq!(resp.status(), 201);
}

test::set_json() envía JSON en la solicitud. ✔ assert_eq!(resp.status(), 201); verifica que el código de estado sea 201 Created.

📌 3️⃣ Testeando la consulta de pokémon #

#[Actix_web::test]
async fn test_listar_elemento() {
    let pool = establecer_conexion_test().await;
    let app = test::init_service(
        App::new()
            .app_data(web::Data::new(pool.clone()))
            .route("/elementoes", web::get().to(listar_elemento)),
    )
    .await;

    let req = test::TestRequest::get().uri("/elementoes").to_request();
    let resp = test::call_service(&app, req).await;
    assert!(resp.status().is_success());
}

Simula una petición GET a /elementoes para verificar la consulta.


📦 Configurando una base de datos de prueba #

Para evitar modificar datos reales, creamos una base de datos de prueba.

📌 1️⃣ Definir una Conexión de Prueba

use sqlx::PgPool;
use std::env;

async fn establecer_conexion_test() -> PgPool {
    dotenv::dotenv().ok();
    let database_url = env::var("DATABASE_URL_TEST").expect("DATABASE_URL_TEST no está configurado");
    PgPool::connect(&database_url).await.expect("No se pudo conectar a la BD de prueba")
}

📌 2️⃣ Crear la Base de Datos de Prueba

CREATE DATABASE pokedex_test;

Añadimos DATABASE_URL_TEST en .env.test:

DATABASE_URL_TEST=postgres://rustaceo:password@localhost/pokedex_test

🏆 Conclusión #

Hemos añadido una capa de pruebas automatizadas a nuestra API REST en Rust.

🎯 Resumen de lo aprendido: ✔ Configuración de tests unitarios y de integración. ✔ Simulación de peticiones HTTP con actix-web::test. ✔ Validación de la persistencia con una base de datos de prueba.

🔮 Próximo paso: Ampliar la API con autenticación y control de acceso.

¡Nos vemos en la siguiente entrega, Rustaceos!