Introducción: Por qué las pruebas en Rust son la base del código confiable
Rust es un lenguaje de programación de sistemas que se destaca por su seguridad de memoria y rendimiento. Sin embargo, incluso el compilador más estricto no garantiza que su programa haga exactamente lo que necesita. Aquí es donde entran en escena las pruebas. En Rust, el soporte integrado para pruebas es uno de los mejores entre los lenguajes modernos. Puede escribir unit-tests (pruebas unitarias), integration-tests (pruebas de integración) y e2e-tests (pruebas de extremo a extremo) sin necesidad de bibliotecas de terceros.
En este artículo, analizaremos los tres niveles de pruebas con ejemplos prácticos. Aprenderá cómo escribir pruebas, cómo ejecutarlas con cargo test y cómo organizar la infraestructura de pruebas para proyectos grandes.
1. Unit-tests en Rust: probando funciones y módulos
Las unit-tests verifican el funcionamiento de funciones o métodos individuales de forma aislada. En Rust, es común colocar las unit-tests directamente en el archivo de código fuente, dentro del módulo tests con la anotación #[cfg(test)]. Esto permite que las pruebas tengan acceso a las funciones privadas del módulo.
1.1. Primer unit-test
Creemos una biblioteca simple para cálculos y escribamos pruebas para ella.
// src/lib.rspub fn add(a: i32, b: i32) -> i32 { a + b}
pub fn divide(a: f64, b: f64) -> Result<f64, String> { if b == 0.0 { Err(String::from("División por cero")) } else { Ok(a / b) }}
#[cfg(test)]mod tests { use super::*;
#[test] fn test_add() { assert_eq!(add(2, 3), 5); }
#[test] fn test_divide_success() { let result = divide(10.0, 2.0).unwrap(); assert_eq!(result, 5.0); }
#[test] fn test_divide_by_zero() { let result = divide(10.0, 0.0); assert!(result.is_err()); assert_eq!(result.unwrap_err(), "División por cero"); }}Ejecute las pruebas con el comando cargo test. Verá marcas verdes si todas las pruebas pasan.
1.2. Atributos de las pruebas
#[test]— indica que la función es una prueba.#[should_panic]— la prueba se considera exitosa si la función entra en pánico.#[ignore]— desactiva temporalmente la prueba.
#[test]#[should_panic(expected = "mensaje de pánico")]fn test_panic() { panic!("mensaje de pánico");}
#[test]#[ignore = "Prueba desactivada hasta corregir el bug #42"]fn test_expensive() { // Prueba larga}2. Integration-tests: verificando la interacción de componentes
Las integration-tests verifican el funcionamiento de varios módulos o dependencias externas juntos. En Rust, se colocan en un directorio separado tests/ a nivel de la raíz del proyecto. Cada archivo en esta carpeta es un crate de prueba independiente que solo ve la API pública de su biblioteca.
2.1. Estructura de las integration-tests
Cree la carpeta tests/ y el archivo tests/integration_test.rs:
// tests/integration_test.rsuse my_crate::add; // Nombre de su crate
#[test]fn test_add_integration() { // Verificamos que la función funciona como parte de la biblioteca let result = add(10, 20); assert_eq!(result, 30);}
#[test]fn test_divide_integration() { // Prueba con múltiples llamadas let a = 100.0; let b = 4.0; let result = my_crate::divide(a, b).unwrap(); assert!(result > 0.0); assert_eq!(result, 25.0);}2.2. Módulos comunes para integration-tests
Si necesita reutilizar código entre integration-tests, cree el archivo tests/common/mod.rs. Esto no se interpretará como un archivo de prueba, pero podrá importarlo desde otras pruebas.
// tests/common/mod.rspub fn setu
```