Fundamentos de Pruebas de Software en Rust

RustRustBeginner
Practicar Ahora

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

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

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.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL rust(("Rust")) -.-> rust/ControlStructuresGroup(["Control Structures"]) rust(("Rust")) -.-> rust/DataStructuresandEnumsGroup(["Data Structures and Enums"]) rust(("Rust")) -.-> rust/BasicConceptsGroup(["Basic Concepts"]) rust(("Rust")) -.-> rust/DataTypesGroup(["Data Types"]) rust(("Rust")) -.-> rust/FunctionsandClosuresGroup(["Functions and Closures"]) rust(("Rust")) -.-> rust/MemorySafetyandManagementGroup(["Memory Safety and Management"]) rust/BasicConceptsGroup -.-> rust/variable_declarations("Variable Declarations") rust/BasicConceptsGroup -.-> rust/mutable_variables("Mutable Variables") rust/DataTypesGroup -.-> rust/boolean_type("Boolean Type") rust/DataTypesGroup -.-> rust/string_type("String Type") rust/ControlStructuresGroup -.-> rust/for_loop("for Loop") rust/FunctionsandClosuresGroup -.-> rust/function_syntax("Function Syntax") rust/FunctionsandClosuresGroup -.-> rust/expressions_statements("Expressions and Statements") rust/MemorySafetyandManagementGroup -.-> rust/lifetime_specifiers("Lifetime Specifiers") rust/DataStructuresandEnumsGroup -.-> rust/method_syntax("Method Syntax") subgraph Lab Skills rust/variable_declarations -.-> lab-99339{{"Fundamentos de Pruebas de Software en Rust"}} rust/mutable_variables -.-> lab-99339{{"Fundamentos de Pruebas de Software en Rust"}} rust/boolean_type -.-> lab-99339{{"Fundamentos de Pruebas de Software en Rust"}} rust/string_type -.-> lab-99339{{"Fundamentos de Pruebas de Software en Rust"}} rust/for_loop -.-> lab-99339{{"Fundamentos de Pruebas de Software en Rust"}} rust/function_syntax -.-> lab-99339{{"Fundamentos de Pruebas de Software en Rust"}} rust/expressions_statements -.-> lab-99339{{"Fundamentos de Pruebas de Software en Rust"}} rust/lifetime_specifiers -.-> lab-99339{{"Fundamentos de Pruebas de Software en Rust"}} rust/method_syntax -.-> lab-99339{{"Fundamentos de Pruebas de Software en Rust"}} end

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.