web-dev-qa-db-fra.com

Pourquoi les paramètres assertEquals () sont-ils dans l'ordre (attendu, réel)?

Pourquoi tant de assertEquals() ou fonctions similaires prennent-elles la valeur attendue comme premier paramètre et la réelle comme seconde?
Cela me semble contre-intuitif, alors y a-t-il une raison particulière à cet ordre inhabituel?

59
jor

Parce que les auteurs avaient 50% de chances de correspondre à votre intuition.

En raison de l'autre surcharge

assertWhatever(explanation, expected, actual)

Et l'explication, qui fait partie de ce que vous savez, va avec l'attendu, qui est ce que vous savez, par opposition à la réalité, que vous ne connaissez pas au moment où vous écrivez le code.

23
bmargulies

Je suis d'accord avec le consensus que la cohérence est n ° 1, mais le comportement de comparaison des dictionnaires peut être un point de données utile si vous évaluez cette question.

Quand je vois un "+" sur un diff, je le lis comme "la procédure en cours de test l'a ajouté". Encore une fois, les préférences personnelles s'appliquent.

Remarque: J'ai utilisé des touches alphabétiques et allongé le dictionnaire afin que seule une touche centrale change pour la clarté de l'exemple. D'autres scénarios affichent des différences plus obscurcies. A noter également, assertEqual utilise assertDictEqual in> = 2.7 and> = 3.1

exl.py

from unittest import TestCase


class DictionaryTest(TestCase):

    def test_assert_order(self):
        self.assertEqual(
            {
                'a_first_key': 'value',
                'key_number_2': 'value',
                'z_last_key': 'value',
                'first_not_second': 'value',
            },
            {
                'a_first_key': 'value',
                'key_number_2': 'value',
                'z_last_key': 'value',
                'second_not_first': 'value',
            }
        )

Production:

$ python -m unittest exl
F
======================================================================
FAIL: test_assert_order (exl.DictionaryTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "exl.py", line 18, in test_assert_order
    'second_not_first': 'value',
AssertionError: {'a_first_key': 'value', 'z_last_key': 'value', 'key_number_2': 'value', 'first_ [truncated]... != {'a_first_key': 'value', 'z_last_key': 'value', 'key_number_2': 'value', 'second [truncated]...
  {'a_first_key': 'value',
-  'first_not_second': 'value',
   'key_number_2': 'value',
+  'second_not_first': 'value',
   'z_last_key': 'value'}

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (failures=1)
5
whp

La convention de test xUnit est attendue/réelle. Donc, pour beaucoup, c'est l'ordre naturel puisque c'est ce qu'ils ont appris.

Fait intéressant, dans une rupture avec la convention pour un framework xUnit, qunit va pour réel/attendu. Au moins avec javascript, vous pouvez simplement créer une nouvelle fonction qui encapsule l'ancienne et lui affecter la variable d'origine:

var qunitEquals = equals;
equals = function(expected, actual, message) {
    qunitEquals(actual, expected, message);
};
2
Ed Sykes

C'est un sujet très intéressant, et beaucoup de réponses très pédagogiques ici aussi! Voici ce que j'apprends d'eux:

  1. Intuitif/contre-intuitif peut être considéré comme subjectif, donc peu importe l'ordre dans lequel il a été défini à l'origine, peut-être 50% d'entre nous ne seraient pas satisfaits .

  2. Personnellement, j'aurais préféré qu'il soit conçu comme assertEqual(actual, expected), car, étant donné la similitude conceptuelle entre assert et if, je souhaiterais que cela suive la norme de if actual == expect, Par exemple, if a == 1 .

    (PS: Il est vrai qu'il existe des opinions différentes qui invite à écrire la déclaration if dans "l'ordre inverse", c'est-à-dire if(1==a) {...} , afin de vous empêcher d'en manquer accidentellement =. Mais ce style était loin d'être la norme, même dans le monde C/C++. Et s'il vous arrive d'écrire Python code, vous n'êtes pas vulnérable à cette coquine coquine en premier lieu, car if a = 1 n'est pas valide en Python.)

  3. La raison convaincante pratique de faire assertEqual(expect, actual), est que la bibliothèque la plus unitaire de votre langue suit probablement déjà cet ordre pour générer un message d'erreur lisible. Par exemple, en Python, lorsque vous effectuez assertEqual(expected_dictionary, actual_dictionary), nittest affichera les clés manquantes en réalité avec le préfixe -, Et les clés supplémentaires avec le préfixe + =, comme lorsque vous faites un git diff old_branch new_branch.

    Intuitif ou non, c'est la raison la plus convaincante pour s'en tenir à l'ordre assertEqual(expected, actual). Si vous ne l'aimez pas, vous feriez mieux de l'accepter, car "la praticité bat la pureté" .

  4. Enfin, si vous avez besoin d'un moyen pour vous aider à vous souvenir de l'ordre, cette réponse compare assertEqual(expected_result, actual_calculation) à l'ordre des instructions d'affectation result = calculate(...). Cela peut être un bon moyen de MÉMORISER le comportement de facto, mais à mon humble avis ce n'est pas le raisonnement incontestable de cet ordre qui est plus intuitif.

Alors voilà. Heureux assertEqual(expect, actual)!

2
RayLuo

Le réponse de Kent Beck , créateur de SUnit et JUnit (d'où peut-être l'origine de cette convention), est:

Ligne un tas d'assertEquals dans une rangée. S'y attendre en premier les rend plus lisibles.

Cependant, c'est tellement opposé à ma propre expérience que je dois me demander si je ne le comprends pas. Voici ce que je vois souvent dans les tests:

assertEquals(12345, user.getId());
assertEquals("kent", user.getUsername());
assertEquals("Kent Beck", user.getName());

Je pense que cela se lirait mieux avec la valeur réelle en premier. Cela rassemble davantage de données répétitives, alignant les appels de méthode dont nous testons les valeurs:

assertEquals(user.getId(), 12345);
assertEquals(user.getUsername(), "kent");
assertEquals(user.getName(), "Kent Beck");

(Et il y a d'autres raisons pour lesquelles je préfère cette commande, mais pour les besoins de cette question sur pourquoi c'est le contraire, le raisonnement de Kent semble être le répondre, même si je ne l'ai pas compris.)

1
Chris Povirk

Un objectif ultérieur de assertEqual() est de faire la démonstration du code pour les lecteurs humains. Parce que l'appel de fonction le plus simple est result = function(parameters), on s'habitue à penser à valeur de retour à gauche et appel à droite.

Ainsi, un test qui documente une fonction afficherait un littéral à gauche et un appel à droite.

assertEqual(15, sum((1,2,3,4,5)))

C'est-à-dire (attendu, réel). De même avec une expression.

assertEqual(4, 2 + 2)
1
Bob Stein