Fundamentos de Pruebas de Software en Rust

Beginner

This tutorial is from open-source community. Access the source code

Introducción

En este laboratorio, exploraremos la importancia de las pruebas en el desarrollo de software utilizando Rust y cómo escribir diferentes tipos de pruebas, como pruebas unitarias e integrales. También aprenderemos sobre la organización de las pruebas en proyectos Rust y cómo ejecutarlas utilizando el comando cargo test. Además, discutiremos los posibles problemas que pueden surgir al ejecutar las pruebas de manera concurrente y proporcionaremos un ejemplo para ilustrarlo.

Nota: Si el laboratorio no especifica un nombre de archivo, puede utilizar cualquier nombre de archivo que desee. Por ejemplo, puede utilizar main.rs, compilarlo y ejecutarlo con rustc main.rs &&./main.

Pruebas

Como sabemos, las pruebas son esenciales para cualquier software. Rust tiene un soporte de primera clase para las pruebas unitarias e integrales (ver este capítulo en TRPL).

A partir de los capítulos de pruebas vinculados anteriormente, vemos cómo escribir pruebas unitarias e integrales. Organizacionalmente, podemos colocar las pruebas unitarias en los módulos que se prueban y las pruebas integrales en su propio directorio tests/:

foo
├── Cargo.toml
├── src
│   └── main.rs
│   └── lib.rs
└── tests
    ├── my_test.rs
    └── my_other_test.rs

Cada archivo en tests es una prueba integral independiente, es decir, una prueba que está destinada a probar su biblioteca como si se estuviera llamando desde una caja dependiente.

El capítulo de Pruebas se detalla sobre los tres diferentes estilos de pruebas: Unitarias, Documentación y Integrales.

cargo naturalmente proporciona una forma fácil de ejecutar todas sus pruebas:

$ cargo test

Debería ver una salida como esta:

[object Object]

También puede ejecutar las pruebas cuyo nombre coincide con un patrón:

$ cargo test test_foo
[object Object]

Una advertencia: Cargo puede ejecutar múltiples pruebas concurrentemente, así que asegúrese de que no se interrumpen mutuamente.

Un ejemplo de cómo esta concurrencia puede causar problemas es si dos pruebas escriben a un archivo, como a continuación:

#[cfg(test)]
mod tests {
    // Importa los módulos necesarios
    use std::fs::OpenOptions;
    use std::io::Write;

    // Esta prueba escribe en un archivo
    #[test]
    fn test_file() {
        // Abre el archivo ferris.txt o lo crea si no existe.
        let mut file = OpenOptions::new()
         .append(true)
         .create(true)
         .open("ferris.txt")
         .expect("Failed to open ferris.txt");

        // Imprime "Ferris" 5 veces.
        for _ in 0..5 {
            file.write_all("Ferris\n".as_bytes())
             .expect("Could not write to ferris.txt");
        }
    }

    // Esta prueba intenta escribir en el mismo archivo
    #[test]
    fn test_file_also() {
        // Abre el archivo ferris.txt o lo crea si no existe.
        let mut file = OpenOptions::new()
         .append(true)
         .create(true)
         .open("ferris.txt")
         .expect("Failed to open ferris.txt");

        // Imprime "Corro" 5 veces.
        for _ in 0..5 {
            file.write_all("Corro\n".as_bytes())
             .expect("Could not write to ferris.txt");
        }
    }
}

Aunque la intención es obtener lo siguiente:

$ cat ferris.txt
Ferris
Ferris
Ferris
Ferris
Ferris
Corro
Corro
Corro
Corro
Corro

Lo que realmente se coloca en ferris.txt es esto:

$ cargo test test_foo
Corro
Ferris
Corro
Ferris
Corro
Ferris
Corro
Ferris
Corro
Ferris

Resumen

¡Felicitaciones! Has completado el laboratorio de Pruebas. Puedes practicar más laboratorios en LabEx para mejorar tus habilidades.