Probando pánico en Rust: cómo escribir pruebas para situaciones de emergencia

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

El pánico no es una sentencia de muerte: ¿por qué probar situaciones de emergencia en Rust?

\n\n

Imagine: está escribiendo en Rust una biblioteca para trabajar con dinero. La función divide_money(amount: u64, parts: u64) debería entrar en pánico si parts es igual a cero — ya que no se puede dividir por cero, y no quiso devolver Option o Result por razones arquitectónicas. ¿Cómo asegurarse de que este pánico realmente ocurre? O está desarrollando un analizador que debería fallar al recibir datos incorrectos. Sin pruebas de pánico, corre el riesgo de tener errores "silenciosos" que solo se manifestarán en producción.

\n\n

En Rust, probar el pánico no es solo una "opción agradable", sino una herramienta importante para crear código confiable. En este artículo, analizaremos cómo escribir pruebas unitarias que verifiquen que su código realmente entra en pánico en las situaciones adecuadas. Aprenderá sobre la macro #[should_panic], sus matices, alternativas como std::panic::catch_unwind y las mejores prácticas. ¡Allá vamos!

\n\n

Enfoque clásico: la macro #[should_panic]

\n\n

La forma más simple y común de verificar un pánico en Rust es usar el atributo #[should_panic]. Se coloca sobre la función de prueba y le dice al compilador: "Esta prueba se considera exitosa solo si ocurre un pánico dentro".

\n\n

Ejemplo básico

\n\n
// lib.rs o tests/unit_tests.rs\nfn divide(a: i32, b: i32) -> i32 {\n    if b == 0 {\n        panic!("¡División por cero!");\n    }\n    a / b\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    #[should_panic]\n    fn test_divide_by_zero() {\n        divide(10, 0);\n    }\n}
\n\n

Ejecute las pruebas con el comando cargo test — y verá un resultado verde. Si no ocurre un pánico (por ejemplo, si elimina la verificación de cero), la prueba fallará con el mensaje: "test test_divide_by_zero ... FAILED (expected to panic)".

\n\n

Verificación del mensaje exacto de pánico

\n\n

A veces es necesario asegurarse no solo del hecho del pánico, sino de su mensaje específico. Esto ayuda a evitar falsos positivos, cuando el pánico ocurre debido a otro error. Use el parámetro adicional expected:

\n\n
#[test]\n#[should_panic(expected = "¡División por cero!")]\nfn test_divide_by_zero_with_message() {\n    divide(10, 0);\n}\n\n// Esta prueba no pasará — el mensaje es diferente\n#[test]\n#[should_panic(expected = "¡División por cero!")]\nfn test_other_panic() {\n    panic!("Un error completamente diferente");\n}
\n\n

Importante: expected verifica que el mensaje de pánico contenga la cadena especificada (no que sea exactamente igual). Esto es útil para mensajes largos.

\n\n

Enfoque avanzado: std::panic::catch_unwind

\n\n

A veces #[should_panic] no es suficiente. Por ejemplo, si desea verificar que el pánico ocurrió en un lugar específico y no en otro, o si necesita probar código asíncrono. Para estos casos existe std::panic::catch_unwind — una función que intercepta el pánico y lo devuelve como Result.

\n\n

Cómo funciona

\n\n
use std::panic;\n\nfn risky_function(should_panic: bool) {\n    if should_panic {\n        panic!("¡Emergencia!");\n    }\n}\n\n#[test]\nfn test_catch_panic() {\n    let result = panic::catch_unwind(|| {\n        risky_function(true);\n    });\n    assert!(result.is_err()); // Verificamos que hubo pánico\n}\n\n#[test]\nfn test_no_panic() {\n    let result = panic::catch_unwind(|| {\n        risky_function(false);\n    });\n    assert!(result.is_ok()); // No hubo pánico\n}
\n\n

Verificación del tipo de pánico

\n\n

Con catch_unwind incluso se puede verificar que el pánico contenga un valor específico (no solo una cadena):

\n\n
use std::panic;\nuse std::any::Any;\n\nfn panic_with_number() {\n    panic!(42); // Sí, se puede — ¡pánico con un número!\n}\n\n#[test]\nfn test_panic_with_number() {\n    let resu                

Blogs

Book Recommendations