490 votes

Python unittest - l'opposé de assertRaises ?

Je veux écrire un test pour établir qu'une exception n'est pas levée dans une circonstance donnée.

Il est simple de tester si une Exception est soulevée...

sInvalidPath=AlwaysSuppliesAnInvalidPath()
self.assertRaises(PathIsNotAValidOne, MyObject, sInvalidPath) 

... mais comment faire le en face de .

Quelque chose comme ça, c'est ce que je cherche...

sValidPath=AlwaysSuppliesAValidPath()
self.assertNotRaises(PathIsNotAValidOne, MyObject, sValidPath)

9 votes

Vous pouvez toujours vous contenter de faire ce qui semble fonctionner dans le test. S'il y a une erreur, elle apparaîtra (comptée comme une erreur, plutôt que comme un échec). Bien sûr, cela suppose qu'il ne soulève pas n'importe quelle erreur, plutôt qu'un type d'erreur défini. Sinon, je suppose que vous devez écrire votre propre test.

2 votes

0 votes

Il s'avère que l'on peut en fait mettre en place une assertNotRaises qui partage 90% de son code/comportement avec assertRaises en environ ~30 lignes de code. Voir ma réponse ci-dessous pour les détails.

535voto

DGH Points 3284
def run_test(self):
    try:
        myFunc()
    except ExceptionType:
        self.fail("myFunc() raised ExceptionType unexpectedly!")

47 votes

@hiwaylon - Non, c'est la solution correcte en fait. La solution proposée par user9876 est conceptuellement défectueuse : si vous testez la non-levée de disons ValueError pero ValueError est au contraire soulevé, votre test doit sortir avec une condition d'échec, et non d'erreur. D'un autre côté, si en exécutant le même code, vous obtenez une erreur de type KeyError ce serait une erreur, pas un échec. En python - différemment de certains autres langages - les exceptions sont couramment utilisées pour le flux de contrôle, c'est pourquoi nous avons la fonction except <ExceptionName> syntaxe en effet. À cet égard, la solution de l'utilisateur9876 est tout simplement fausse.

0 votes

@mac - Est-ce également une solution correcte ? stackoverflow.com/a/4711722/6648326

1 votes

Celui-ci a l'effet malheureux de montrer une couverture < 100% (l'exception ne se produira jamais) pour les tests.

84voto

S.Lott Points 207588

Bonjour - Je veux écrire un test pour établir qu'une exception n'est pas levée dans une circonstance donnée.

C'est l'hypothèse par défaut : les exceptions ne sont pas levées.

Si vous ne dites rien d'autre, c'est supposé dans chaque test.

Vous n'avez pas besoin d'écrire une assertion pour cela.

11 votes

@IndradhanushGupta La réponse acceptée rend le test plus pythique que celui-ci. L'explicite est mieux que l'implicite.

38 votes

Aucun autre commentateur n'a souligné por qué cette réponse est fausse, mais pour la même raison que la réponse de user9876 : les échecs et les erreurs sont des choses différentes dans le code de test. Si votre fonction étaient pour lancer une exception pendant un test qui n'affirme pas, le cadre de test traiterait cela comme une erreur d'identification. erreur plutôt qu'une absence d'affirmation.

2 votes

@CoreDumpError Je comprends la différence entre un échec et une erreur, mais cela ne vous obligerait-il pas à entourer chaque test avec une construction try/exception ? Ou recommandez-vous de ne le faire que pour les tests qui lèvent explicitement une exception sous une certaine condition (ce qui signifie en gros que l'exception est attendu ).

58voto

user9876 Points 5385

Il suffit d'appeler la fonction. Si elle lève une exception, le cadre de test unitaire le signalera comme une erreur. Vous pouvez ajouter un commentaire, par exemple :

sValidPath=AlwaysSuppliesAValidPath()
# Check PathIsNotAValidOne not thrown
MyObject(sValidPath)

0 votes

Merci pour cela. Comme vous le dites, cela fonctionne parfaitement bien et je n'y avais pas pensé avant que vous ne le mentionniez. J'ai fini par préférer l'approche de DGH, mais merci quand même.

42 votes

Les échecs et les erreurs sont conceptuellement différents. En outre, étant donné qu'en python les exceptions sont couramment utilisées pour le flux de contrôle, il sera très difficile de comprendre au premier coup d'oeil (=sans explorer le code de test) si vous avez cassé votre logique ou votre code...

3 votes

Soit votre test est réussi, soit il ne l'est pas. S'il ne réussit pas, tu vas devoir le réparer. Qu'il soit signalé comme un "échec" ou une "erreur" n'a pas d'importance. Il y a une différence : avec ma réponse, vous verrez la trace de la pile, ce qui vous permettra de voir où PathIsNotAValidOne a été lancé ; avec la réponse acceptée, vous n'aurez pas cette information, ce qui rendra le débogage plus difficile. (En supposant Py2 ; je ne sais pas si Py3 est meilleur à ce sujet).

18voto

glaucon Points 959

Je suis le posteur original et j'ai accepté la réponse ci-dessus de la DGH sans l'avoir utilisée au préalable dans le code.

Une fois que je l'ai utilisé, je me suis rendu compte qu'il avait besoin d'être un peu modifié pour faire ce dont j'avais besoin (pour être juste envers DGH, il a dit "ou quelque chose de similaire").

J'ai pensé qu'il valait la peine de poster la modification ici pour le bénéfice des autres :

    try:
        a = Application("abcdef", "")
    except pySourceAidExceptions.PathIsNotAValidOne:
        pass
    except:
        self.assertTrue(False)

Ce que j'essayais de faire ici était de m'assurer que si une tentative était faite pour instancier un objet Application avec un second argument d'espaces, la pySourceAidExceptions.PathIsNotAValidOne serait levée.

Je pense que l'utilisation du code ci-dessus (fortement inspiré de la réponse de DGH) le fera.

2 votes

Puisque vous clarifiez votre question et n'y répondez pas, vous auriez dû la modifier (et non y répondre).

18 votes

Il semble que ce soit tout le contraire du problème initial. self.assertRaises(PathIsNotAValidOne, MyObject, sInvalidPath) devrait faire l'affaire dans ce cas.

1voto

hiwaylon Points 153

Si vous passez une classe d'exception à assertRaises() un gestionnaire de contexte est fourni. Cela peut améliorer la lisibilité de vos tests :

# raise exception if Application created with bad data
with self.assertRaises(pySourceAidExceptions.PathIsNotAValidOne):
    application = Application("abcdef", "")

Cela vous permet de tester les cas d'erreur dans votre code.

Dans ce cas, vous testez le PathIsNotAValidOne est soulevé lorsque vous passez des paramètres invalides au constructeur de l'application.

1 votes

Non, cela n'échouera que si l'exception n'est pas soulevée dans le bloc du gestionnaire de contexte. Peut être facilement testé par 'with self.assertRaises(TypeError) : raise TypeError', qui passe.

0 votes

@MatthewTrevor Bon appel. Si je me souviens bien, plutôt que de tester que le code s'exécute correctement, c'est-à-dire qu'il ne lève pas, je suggérais de tester les cas d'erreur. J'ai modifié la réponse en conséquence. J'espère pouvoir gagner un +1 pour sortir du rouge :)

0 votes

Remarque, ceci est également valable pour Python 2.7 et plus : docs.python.org/2/library/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X