En pocas palabras
Herramienta de mutation testing para comprobar la capacidad de detección de pruebas en Rust.
Qué problema resuelve
Pruebas que ejecutan funciones pero no detectan resultados incorrectos.
Cuándo utilizarlo
Bibliotecas Rust y lógica de dominio. Ejemplo: evaluar límites de una función de cálculo.
De la idea a la práctica
Funcionamiento, ejemplos y decisiones para pasar al siguiente nivel.
En el Radar compartido: Trial (valoración de la edición aportada).
Cómo funciona
Introduce cambios temporales como sustitución de retornos u operadores y observa qué pruebas fallan. Reporta mutantes detectados, supervivientes y resultados no concluyentes.
Ejemplo de uso
Ejecutar cargo mutants sobre una copia de trabajo preparada. Si sobrevive un cambio en una condición de descuento, añadir una prueba de frontera que compruebe el importe final.
Qué mutaciones genera
| Mutación | Ejemplo |
|---|---|
| Sustituir el cuerpo de una función | Devolver Default::default(), 0, true, Ok(()) o vec![] |
| Operadores binarios | + ↔ -, < ↔ <=, == ↔ !=, && ↔ ` |
| Operadores unarios | Eliminar ! o - |
Brazos de match |
Eliminar o sustituir un brazo |
Cada mutante se compila y se ejecuta la suite. Un mutante sobreviviente indica código cuyo comportamiento no está protegido por ninguna prueba.
Uso habitual
cargo install --locked cargo-mutants
cargo mutants --list # ver mutantes sin ejecutarlos
cargo mutants # ejecutar todo el árbol
cargo mutants -f src/intereses.rs # acotar a un archivo
cargo mutants --in-diff cambios.diff # solo el código cambiado (útil en PR)
cargo mutants -j 4 # paralelizar
Los resultados quedan en mutants.out/: caught.txt, missed.txt, timeout.txt y unviable.txt (no compila).
Leer los resultados
| Estado | Significado | Acción |
|---|---|---|
| Caught | Alguna prueba falla con el mutante | Ninguna |
| Missed | Todas las pruebas pasan | Añadir una aserción o reconocer código sin valor |
| Timeout | La suite no termina (bucle infinito) | Suele contar como detectado; revisar timeouts |
| Unviable | El mutante no compila | Ignorar |
Excluir lo que no aporta
#[mutants::skip] // logging o código defensivo sin comportamiento observable
fn registrar_auditoria(evento: &Evento) { /* … */ }
También se pueden excluir archivos o funciones en .cargo/mutants.toml (exclude_globs, exclude_re).
Integración en CI
Ejecutar todo el árbol en cada PR suele ser lento. Una estrategia equilibrada: --in-diff sobre el cambio del pull request y un recorrido completo nocturno o semanal, con sharding (--shard 1/4) en varios runners. La técnica general se describe en mutation testing.
Criterios de implementación
Partir de una suite estable y revisar equivalentes. Priorizar módulos críticos localmente y reservar recorridos completos para CI según su duración.
Continúa en las fuentes
Documentación oficial para verificar detalles y explorar más.