Y a-t-il une valeur à écrire un test unitaire qui est un sous-ensemble d'un autre test?

15

Pour donner un exemple légèrement artificiel, disons que je veux tester qu'une fonction renvoie deux nombres et que le premier est plus petit que le second:

def test_length():
    result = my_function()
    assert len(result) == 2

def test_order()
    a, b = my_function()
    assert a < b

Ici, en cas d' test_lengthéchec, test_orderéchouera également. Est-ce une meilleure pratique d'écrire test_lengthou de l'ignorer?

EDIT: notez que dans cette situation, les deux tests sont pour la plupart indépendants l'un de l'autre, chacun peut être exécuté de manière isolée, ou ils peuvent être exécutés dans l'ordre inverse, cela n'a pas d'importance. Donc, aucune de ces anciennes questions

est un double de celui ci-dessus.

Mihai
la source
2
@ GlenH7 - cela semble être une question différente. L'autre est "si vous avez des fonctions où les Aappels Bet renvoie le même résultat, si vous testez les deux Aet B". Il s'agit plus du chevauchement des tests que des fonctions testées. (Bien que ce soit déroutant car ils sont actuellement nommés).
Telastyn
1
Strictement parlant, ces tests ne se chevauchent pas vraiment (ils ne dépendent pas du succès). Une fonction lambda: type('', (), {'__len__': lambda self: 2})()passera la première, mais pas la seconde.
liori
1
@ GlenH7: FWIW, je n'ai pas vraiment changé la question, j'ai juste essayé de la rendre sûre contre les moucherons ;-) (@gnat: pas d'offense, je plaisante!)
Doc Brown

Réponses:

28

Il peut y avoir de la valeur, mais c'est un peu l'odeur. Soit vos tests ne sont pas bien isolés (car ils test_ordertestent vraiment deux choses), soit vous êtes trop dogmatique dans vos tests (faire de deux tests tester la même chose logique).

Dans l'exemple, je fusionnerais les deux tests ensemble. Oui, cela signifie que vous avez plusieurs assertions. Dommage. Vous testez toujours une seule chose - le résultat de la fonction. Parfois, dans le monde réel, cela signifie faire deux vérifications.

Telastyn
la source
J'ai voté contre votre réponse bien qu'elle manque un point mineur. En Python, le deuxième test échouera également lorsqu'il my_functionne renvoie pas exactement deux valeurs, sans assertion - car l'affectation entraînera une exception. Il n'est donc pas nécessaire d'utiliser plusieurs assertions et deux vérifications pour tester la même chose.
Doc Brown
@DocBrown - J'en ai supposé autant, mais je ne connais pas les messages d'erreur de Python pour savoir s'il peut être utile d'avoir un échec d'assertion au lieu d'une exception / erreur aléatoire pour des raisons informatives.
Telastyn du
Je pense que dans la situation actuelle, le message d'erreur serait probablement suffisamment informatif. Mais en général, cela pourrait ne pas être le cas. C'est pourquoi je conviens avec vous que l'approche générale pour un cas comme celui-ci devrait être de penser à une "fusion", en utilisant deux assertions. S'il s'avère que le test peut être encore simplifié en omettant l'un des assert, très bien.
Doc Brown
5
@DocBrown L'autre valeur de la vérification explicite avec une assertion est qu'il devient clair pour quiconque examine le test qu'il s'agit d'un échec du code testé, et non du test lui-même. Lorsque mes tests rencontrent des exceptions inattendues, je dois toujours passer un peu de temps à déterminer lequel est réellement faux.
Chris Hayes
@ChrisHayes: J'ai écrit "il n'y a pas besoin", pas "il n'y a pas de valeur".
Doc Brown
5

Vos tests doivent être explicites. Il n'est pas complètement déduit que si text_length échoue, test_order échoue.

Je ne sais pas comment cela se passe en Python que vous avez publié, mais si len(result)c'est 3, le premier échouera mais le second peut passer (et sinon en Python, alors dans des langues comme JavaScript à coup sûr).

Comme vous l'avez dit, vous voulez tester que la fonction retourne deux nombres et qu'ils sont en ordre. C'est deux tests.

Le fantôme de Madara
la source
1
It's not completely inferred that if text_length fails test_order fails- en Python, il est, vous donnera une exception.
Doc Brown du
Je pense que la question porte sur le cas où l'échec de test_lengthimplique l'échec de test_order. Le fait qu'une paire similaire de tests écrits en Javascript ne se comporterait pas de la même manière que ces deux tests python est en quelque sorte hors de propos.
Dawood dit de réintégrer Monica le
2
@DavidWallace: rappelez-vous, la question était "Est-ce une meilleure pratique d'écrire test_length, ou de le sauter"? En Javascript, vous avez besoin des deux tests pour vous assurer de ne pas manquer un problème (lorsque vous ne fusionnez pas les deux tests en un seul). En Python, vous pouvez en toute sécurité omettre la première (qui est refusée dans la première phrase de cette réponse et déclarée "Je ne suis pas sûr" dans la seconde). Honnêtement, une bonne réponse est différente. Cependant, je n'ai pas déçu cette réponse car la bonne partie est en fait la mention de JavaScript.
Doc Brown
4
@DavidWallace: puisque nous sommes ici un programmers.com, pas sur stackoverflow.com, à mon humble avis, donner une réponse qui pourrait être appliquée à plus d'une langue vaut mieux qu'une réponse uniquement pour une langue particulière. Mais lorsque vous vous référez à une langue spécifique, la réponse doit être correcte à propos de la langue et ne pas mélanger certaines propriétés.
Doc Brown du
1
La chose est, la question est "vaut-il la peine d'écrire un test qui est un sous-ensemble d'un autre test", et cette réponse dit, "dans certaines langues, aucun de vos tests n'est un sous-ensemble de l'autre" et arrive à la conclusion que les deux tests sont donc nécessaire. Il me lit comme si quelqu'un disait, "votre code ne compile pas du tout en C ++, et en plus, en C ++ une fonction ne peut retourner qu'une seule valeur donc vous n'avez pas besoin de la tester en retourne deux. N'écrivez qu'un seul test" ;-)
Steve Jessop
2

La seule valeur test_lengthici est que, si tout passe, sa présence même indique que la "longueur" a été testée.

Il est donc vraiment inutile d'avoir ces deux tests. Gardez seulement test_ordermais pensez à le renommer test_length_and_order.

Soit dit en passant, je trouve l'utilisation de noms qui commencent par testun peu maladroite. Je suis un ardent défenseur des noms de test qui décrivent réellement la condition que vous affirmez.

Dawood dit réintégrer Monica
la source
1
La testverrue est significative pour le framework de test de Python. Ce qui bien sûr n'a pas besoin de changer si vous êtes fan de cela, mais change le poids des arguments nécessaires pour empêcher quelqu'un de l'utiliser ;-)
Steve Jessop
@SteveJessop Oui, j'oublie toujours que c'est nécessaire. Lorsque j'écrivais des tests Python il y a quelque temps, j'ai pris l'habitude de tous les commencer par test_that_, comme test_that_return_values_are_in_orderet ainsi de suite. Peut-être qu'une future version du framework de test contournera cette exigence d'une manière ou d'une autre.
Dawood dit de réintégrer Monica le
@DavidWallace: vous pouvez changer le préfixe si vous le souhaitez.
Lie Ryan
1

Je laisserais ces tests séparés si c'est ainsi qu'ils ont évolué. Oui test_orderest susceptible d'échouer à chaque fois test_length, mais vous savez certainement pourquoi.

Je suis également d'accord pour dire que si le test_orderpremier est apparu et que vous avez trouvé un échec testable, c'est que le résultat n'était peut-être pas deux valeurs, en ajoutant cette vérification en tant que assertet en renommant le test pour avoir du test_length_and_ordersens.

Si vous deviez également vérifier que les types de valeurs renvoyées étaient des entiers, j'inclurais cela dans ce test de résultats "omnibus".

Mais notez maintenant que vous avez une batterie de tests pour le résultat de my_function(). S'il existe plusieurs contextes (ou, plus probablement, plusieurs paramètres) pour tester, cela peut maintenant être un sous-programme pour tester tous les résultats my_function().

Cependant, lorsque j'écris des tests unitaires, je teste normalement le bord et les mauvais cas séparément de la bonne entrée et de la sortie normale (en partie parce que la plupart de mes tests "unitaires" sont souvent des mini tests d'intégration), et souvent avec plusieurs assertions, qui ne sont cassées que dans des tests séparés s'ils échouent d'une manière où je trouve que je veux plus d'informations sans débogage.

Donc , je ne serais probablement commencer par vos tests séparés et d' étendre test_lengthà test_length_and_typeset laisser test_orderséparé, en supposant que celui- ci est considérée comme « traitement normal ».

Mark Hurd
la source