Comment puis-je écrire un test unitaire Rust qui s'assure qu'une panique s'est produite?

J'ai une fonction Rust qui panic est sous certaines conditions et je souhaite écrire un cas de test pour valider si la fonction panique ou non. Je n'ai rien trouvé d'autre que les macros assert! et assert_eq!. Est-il un mécanisme pour tester cela?

Je pourrais générer une nouvelle tâche et vérifier si cette tâche panique ou non. Est-il judicieux?


Renvoyer un Result<T, E> ne convient pas dans mon cas.

Je souhaite ajouter le support pour le trait Add à un Matrix type I suis mise en œuvre. La syntaxe idéale pour un tel ajout ressemblerait à:

let m = m1 + m2 + m3;

m1, m2, m3 sont toutes les matrices. Par conséquent, le type de résultat de add devrait être Matrix. Quelque chose comme ce qui suit serait trop cryptique:

let m = ((m1 + m2).unwrap() + m3).unwrap()

En même temps, la fonction add() doit valider que les deux matrices ajoutées ont la même dimension. Ainsi, add() doit paniquer si les dimensions ne correspondent pas. L'option disponible est panic!().

30
demandé sur Shepmaster 2014-10-20 19:46:32

4 réponses

Vous pouvez trouver la réponse dans test section de la Rouille livre. Plus précisément, vous voulez #[should_panic] attribut:

#[test]
#[should_panic]
fn test_invalid_matrices_multiplication() {
    let m1 = Matrix::new(3, 4);  // assume these are dimensions
    let m2 = Matrix::new(5, 6);
    m1 * m2
}
36
répondu Vladimir Matveev 2015-07-16 13:13:47

Comme Francis Gagné l'a mentionné dans sa réponse, je trouve aussi que l'attribut #[should_panic] n'est pas assez fin pour des tests plus complexes-par exemple, si ma configuration de test échoue pour une raison quelconque (c'est-à-dire que j'ai écrit un mauvais test), je veux qu'une panique soit considérée comme un échec!

, Comme de la Rouille 1.9.0, std::panic::catch_unwind() est disponible. Il vous permet de mettre le code que vous attendez de paniquer dans une fermeture, et seules les paniques lancées par que code seront considérées comme "attendues" (c'est-à-dire un passage test).

#[test]
fn test_something() {
    ... //<-- Any panics here will cause test failure (good)
    let result = std::panic::catch_unwind(|| <expected_to_panic_operation_here>);
    assert!(result.is_err());  //probe further for specific error type here, if desired
}

Notez qu'il ne peut pas attraper les paniques non-déroulantes (par exemple abandonner).

16
répondu U007D 2017-11-15 00:27:19

Si vous voulez affirmer que seule une partie spécifique de la fonction de test échoue, utilisez task::try() et vérifier qu'elle renvoie un Err, par exemple avec is_err(). Dans les fonctions de test complexes, cela permet de s'assurer que le test ne passe pas par erreur en raison d'un échec précoce.

Plusieurs les tests dans la bibliothèque standard Rust elle-même utilisent cette technique.

9
répondu Francis Gagné 2015-02-23 17:14:43

En addendum: la solution proposée par @U007D fonctionne également dans doctests:

/// My identity function that panic for an input of 42.
///
/// ```
/// assert_eq!(my_crate::my_func(23), 23);
///
/// let result = std::panic::catch_unwind(|| my_crate::my_func(42));
/// assert!(result.is_err());
/// ```
pub fn my_func(input: u32) -> u32 {
    if input == 42 {
        panic!("Error message.");
    } else {
        input
    }
}
0
répondu m00am 2018-01-30 10:44:24