Pruebas en Rust: Unit, Integration, E2E a nivel avanzado

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Pruebas avanzadas en Rust: desde unit hasta E2E

Las pruebas son una parte esencial del desarrollo de software de sistema confiable en Rust. Si los conceptos básicos (escribir pruebas simples con #[test]) son familiares para todos, las técnicas avanzadas —trabajar con mocks, parametrización, atributos personalizados y la organización de pruebas E2E— requieren un análisis aparte. En este artículo, veremos cómo llevar las pruebas en Rust a un nivel profesional, utilizando enfoques unit, integration y end-to-end.



1. Pruebas unitarias: mock, parametrización y atributos personalizados

Las pruebas unitarias en Rust generalmente se escriben en el mismo archivo que el código, en el módulo tests. Sin embargo, a nivel avanzado surge la necesidad de aislar el código bajo prueba de las dependencias externas.



Objetos mock con traits

Para crear mocks en Rust, se suelen usar traits. Usted define un trait, lo implementa para el objeto real y para el mock de prueba. Crates populares: mockall y mockito (para HTTP).

use mockall::automock;

#[automock]trait Database { fn get_user(&self, id: u32) -> String;}

fn process_user(db: &dyn Database, id: u32) -> String { let user = db.get_user(id); format!("User: {}", user)}

#[cfg(test)]mod tests { use super::*;

#[test] fn test_process_user() { let mut mock = MockDatabase::new(); mock.expect_get_user() .with(predicate::eq(42)) .returning(|_| "Alice".to_string()); assert_eq!(process_user(&mock, 42), "User: Alice"); }}


Pruebas parametrizadas

Repetir la misma prueba con diferentes datos de entrada es una tarea común. El crate rstest proporciona una solución elegante.

use rstest::rstest;

#[rstest]#[case(2, 3, 5)]#[case(0, 0, 0)]#[case(-1, 1, 0)]fn test_add(#[case] a: i32, #[case] b: i32, #[case] expected: i32) { assert_eq!(a + b, expected);}


Atributos de prueba personalizados

A veces se requiere lógica antes o después de la prueba. Se pueden crear atributos propios mediante macros procedurales, pero es más sencillo usar el crate test-case o ctor para la inicialización.

// Ejemplo con ctor para inicializar un logger#[cfg(test)]mod tests {    use ctor::ctor;

#[ctor] fn init() { // Inicialización única antes de todas las pruebas }}


2. Pruebas de integración: organización y trabajo con dependencias externas

Las pruebas de integración en Rust se encuentran en la carpeta tests/ a nivel de la raíz del proyecto. Cada archivo allí es un crate independiente. Las técnicas avanzadas incluyen módulos compartidos y pruebas con bases de datos o API reales.



Utilidades compartidas para pruebas de integración

Para no duplicar código (por ejemplo, la configuración de un servidor de prueba), cree un módulo common dentro de tests/.

// tests/common/mod.rspub fn setup_test_db() -> String {    // Configuración de la BD de prueba    "connected".to_string()}

// tests/integration_test.rsmod common;

#[test]fn test_db_connection() { let db = common::setup_test_db(); assert_eq!(db, "connected");}


Trabajo con solicitudes HTTP

Para probar clientes HTTP, use mockito — levanta un servidor mock local.

use mockito::{mock, server_url};

#[tokio::test]async fn test_api_call() { let _m = mock("GET", "/users/1") .with_status(200) .with_body(r#"{"id":1,"name":"Alice"}"#) .create();

let client = reqwest::Client::new(); let resp = client.get(&format!("{}/users/1", server_url())) .send() .await .unwrap(); assert!(resp.status().is_success());}


3. Pruebas E2E: ciclo completo de la aplicación

Las pruebas end-to-end verifican el funcionamiento del sistema en su totalidad: desde el in

Blogs

Book Recommendations