• Skip to primary navigation
  • Skip to main content
  • Skip to footer

Codemotion Magazine

We code the future. Together

  • Discover
    • Events
    • Community
    • Partners
    • Become a partner
    • Hackathons
  • Magazine
    • DevOps
    • Carreras tech
    • Frontend
    • Inteligencia Artificial
    • Dev life
    • Desarrollo web
  • Talent
    • Discover Talent
    • Jobs
    • Manifiesto
  • Companies
  • For Business
    • EN
    • IT
    • ES
  • Sign in
ads

Aitor Santanaoctubre 8, 2024 3 min read

Testing en Rust

Testeo
framework, common developer mistakes and errors to avoid.
facebooktwitterlinkedinreddit

En Rust, hacer pruebas está integrado directamente en el lenguaje, lo que facilita verificar el comportamiento del código sin necesidad de frameworks externos. Esto, si aplicas técnicas como TDD, te permite mantener el ciclo de desarrollo ágil y enfocado en la calidad desde el principio.

Para ejecutar un test en Rust, solo necesitas marcar una función como prueba usando el atributo #[test]. Al usar este atributo, Rust sabe que esa función debe ejecutarse cuando corres el comando cargo test , que es la herramienta para gestionar pruebas en Rust.

Recommended article
marzo 5, 2025

¿Realmente necesitas esa Nueva Tecnología o solo quieres usarla?

srjonro

Lenguajes de programación
#[test]
fn foo() {
    // some code...
}Lenguaje del código: Rust (rust)

Funciones de ejemplo

Para mostrar las diferentes aserciones disponibles en Rust, vamos a usar dos funciones sencillas: una que suma dos números y otra que verifica si un número es par.

fn sum(a: i32, b: i32) -> i32 {
    a + b
}

fn is_even(a: i32) -> bool {
    a % 2 == 0
}Lenguaje del código: Rust (rust)

Con estas funciones, podrás ver cómo se usan los distintos macros de aserciones.

Aserciones

En Rust, existen varios macros nativos que puedes usar para hacer pruebas. A continuación, te muestro los más comunes:

assert!

El macro assert! es el más básico y se usa para verificar que una expresión booleana sea verdadera. Si la expresión es falsa, la prueba fallará con un panic.

#[test]
fn check_if_a_number_is_even() {
    assert!(is_even(6));
}Lenguaje del código: Rust (rust)

Este macro es ideal cuando solo necesitas verificar que una condición se cumpla, sin comparar valores específicos.

assert_eq!

El macro assert_eq! compara dos valores para verificar que sean iguales. Si no lo son, la prueba fallará mostrando ambos valores, lo que facilita depurar el problema.

#[test]
fn calculate_the_sum_of_two_numbers() {
    assert_eq!(sum(3, 3), 6);
}Lenguaje del código: Rust (rust)

Funciona para cualquier tipo que implemente el trait PartialEq. Por ejemplo:

#[derive(Debug, PartialEq)]
pub struct Point {
    pub x: i32,
    pub y: i32,
}

#[test]
fn two_points_are_equal() {
    let point1 = Point { x: 1, y: 2 };
    let point2 = Point { x: 1, y: 2 };
    assert_eq!(point1, point2);
}Lenguaje del código: Rust (rust)

assert_ne!

Este macro es el inverso de assert_eq!: compara dos valores y verifica que no sean iguales. Si los valores son iguales, la prueba fallará.

#[test]
fn calculate_the_sum_of_two_numbers_fail() {
    assert_ne!(sum(3, 3), 5);
}Lenguaje del código: Rust (rust)

Aserciones de depuración

Rust también tiene macros de depuración como debug_assert!, debug_assert_eq! y debug_assert_ne!. Estos funcionan igual que los macros anteriores, pero solo se ejecutan en modo debug, no en compilaciones de release. Esto puede ser útil para optimizar el rendimiento.

#[test]
fn calculate_the_sum_of_two_numbers_with_debug() {
    debug_assert_eq!(sum(3, 3), 6);
}

#[test]
fn calculate_the_sum_of_two_numbers_fail_with_debug() {
    debug_assert_ne!(sum(3, 3), 5);
}

#[test]
fn check_if_a_number_is_even_with_debug() {
    debug_assert!(is_even(6));
}Lenguaje del código: Rust (rust)

Otros macros

Además de los macros nativos, puedes usar librerías externas para ampliar las aserciones. Por ejemplo, la librería matches permite verificar si una expresión coincide con un patrón.

use matches::assert_matches;

#[test]
fn check_an_specific_pattern() {
    let resultado: Result<i32, &str> = Ok(10);
    assert_matches!(resultado, Ok(10));
}Lenguaje del código: Rust (rust)

Organización de tests en Rust

Hay dos maneras principales de organizar tus pruebas en Rust: dentro del mismo módulo que el código productivo o en una carpeta separada llamada tests.

Pruebas en el mismo módulo

Si colocas las pruebas en el mismo módulo, puedes probar funciones privadas y mantener las pruebas cerca del código que validan. Esto es útil en proyectos pequeños o para pruebas rápidas.

#[cfg(test)]
fn sum(a: i32, b: i32) -> i32 {
    a + b
}

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

    #[test]
    fn calculate_the_sum_of_two_numbers() {
        assert_eq!(sum(3, 3), 6);
    }
}Lenguaje del código: Rust (rust)

Pruebas en archivos separados

Para proyectos más grandes, es mejor organizar las pruebas en una carpeta tests. Esto te permite mantener el código de producción separado del código de prueba.

use testing_rust::testing_rust::sum;

#[test]
fn calculate_the_sum_of_two_numbers() {
    assert_eq!(sum(3, 3), 6);
}Lenguaje del código: Rust (rust)

Es una buena práctica nombrar estos archivos con el sufijo _test.rs, por ejemplo: foo_test.rs.

Mocks en Rust

Aunque Rust no tiene un sistema de mocks integrado, librerías como mockall permiten crear mocks fácilmente. Los mocks son útiles para simular dependencias externas, como bases de datos o servicios remotos.

Definición de la interfaz

#[derive(Debug, PartialEq)]
pub struct User {
    pub user_id: i32,
    pub name: String,
    pub age: i32,
}

pub trait DatabaseRepository {
    fn find_by_id(&self, user_id: &i32) -> bool;
}Lenguaje del código: Rust (rust)

Uso de mockall para crear un mock

use mockall::mock;
use testing_rust::database::{DatabaseRepository, User};

mock! {
  pub Database {}

  impl DatabaseRepository for Database {
        fn find_by_id(&self, user: &i32) -> bool;
  }
}

#[test]
fn user_exist_in_database() {
    let mut mock_db = MockDatabase::new();
    let user = User { user_id: 1, name: "test data".to_string(), age: 20 };
    mock_db.expect_find_by_id()
        .with(mockall::predicate::eq(user.user_id.clone()))
        .times(1)
        .returning(|_| true);

    let result = is_user_in_database(&mock_db, &user);

    assert!(result);
}

fn is_user_in_database(db: &dyn DatabaseRepository, user: &User) -> bool {
    db.find_by_id(&user.user_id)
}Lenguaje del código: Rust (rust)

En este caso, hemos utilizado el macro mock! para generar un mock de nuestra interfaz DatabaseRepository, y luego lo hemos utilizado para simular el comportamiento de nuestra base de datos.

Si prefieres mantener los mocks dentro del mismo módulo que tu código, puedes usar el atributo #[automock] para generar los mocks directamente a partir de la interfaz:

use mockall::{automock, mock};
use testing_rust::database::User;

#[automock]
trait DatabaseRepository {
    fn find_by_id(&self, user_id: &i32) -> bool;
}Lenguaje del código: Rust (rust)

De esta forma, Rust te proporciona un mock automático sin tener que crear uno manualmente.

Conclusión

Rust proporciona una infraestructura robusta y flexible para pruebas, integrada directamente en el lenguaje. Ya sea que estés escribiendo pruebas unitarias sencillas con assert! o pruebas más complejas con mocks usando mockall, el ecosistema de pruebas en Rust te permite asegurarte de que tu código es confiable y robusto.

Existen muchas más herramientas externas para realizar otros tipos de tests, como proptest para property based testing, o httpmock para hacer tests de integración con apis http. ¡Estas y otras herramientas pueden ser exploradas en un post futuro!

Si queréis revisar más cosillas sobre Rust, podéis hecharle un ojo a estos artículos:

  • Smart Pointers
  • Ownership

Share on:facebooktwitterlinkedinreddit

Tags:Desarrollo web

Aitor Santana
Software Crafter | Desarrollador de Software en LeanMind. Me preocupo en intentar escribir código limpio y mantenible. Para ello utilizó técnicas cómo TDD, código sostenible, patrones de diseño o arquitecturas limpias.
¿Cómo funciona el renderizado en React?
Artículo anterior
Ataques informáticos y cómo defenderse: Man In The Middle
Próximo artículo

Footer

Discover

  • Events
  • Community
  • Partners
  • Become a partner
  • Hackathons

Magazine

  • Tech articles

Talent

  • Discover talent
  • Jobs

Companies

  • Discover companies

For Business

  • Codemotion for companies

About

  • About us
  • Become a contributor
  • Work with us
  • Contact us

Follow Us

© Copyright Codemotion srl Via Marsala, 29/H, 00185 Roma P.IVA 12392791005 | Privacy policy | Terms and conditions