web-dev-qa-db-fra.com

Comment atteindre assertDictEqual avec assertSequenceEqual appliqué aux valeurs

Je sais que lors de l'exécution de assertEqual sur un dictionnaire, assertDictEqual est appelé. De même, assertEqual sur une séquence exécutera assertSequenceEqual.

Cependant, lorsque assertDictEqual compare des valeurs, il ne semble pas utiliser assertEqual, et donc assertSequenceEqual n'est pas appelé.

Considérez les dictionnaires simples suivants:

lst1 = [1, 2]
lst2 = [2, 1]

d1 = {'key': lst1}
d2 = {'key': lst2}

self.assertEqual(lst1, lst2) # True
self.assertEqual(d1, d2) # False ><

Comment puis-je tester des dictionnaires tels que d1 et d2 de telle sorte que leur égalité soit correctement comparée, en appliquant récursivement une sémantique de type assertEqual aux valeurs?

Je veux éviter d'utiliser des modules externes (comme suggéré dans cette question ) si possible, à moins qu'ils ne soient des extensions natives Django extensions.


ÉDITER

Essentiellement, ce que je recherche est une version intégrée de ceci:

def assertDictEqualUnorderedValues(self, d1, d2):
    for k,v1 in d1.iteritems():
        if k not in d2:
            self.fail('Key %s missing in %s'%(k, d2))

        v2 = d2[k]

        if isinstance(v1, Collections.iterable) and not isinstance(v1, basestring):
            self.assertValuesEqual(v1, v2)
        else:
            self.assertEqual(v1, v2)

Le problème avec le code ci-dessus est que les messages d'erreur ne sont pas aussi agréables que l'affirme la fonction intégrée, et il y a probablement des cas Edge que j'ai ignorés (comme je viens de l'écrire du haut de ma tête).

18
sapi

Plutôt que de remplacer assertDictEqual, pourquoi ne triez-vous pas récursivement vos dict en premier?

def deep_sort(obj):
    """
    Recursively sort list or dict nested lists
    """

    if isinstance(obj, dict):
        _sorted = {}
        for key in sorted(obj):
            _sorted[key] = deep_sort(obj[key])

    Elif isinstance(obj, list):
        new_list = []
        for val in obj:
            new_list.append(deep_sort(val))
        _sorted = sorted(new_list)

    else:
        _sorted = obj

    return _sorted

Ensuite, triez et utilisez assertDictEqual normal:

    dict1 = deep_sort(dict1)
    dict2 = deep_sort(dict2)

    self.assertDictEqual(dict1, dict2)

Cette approche a l'avantage de ne pas se soucier du niveau de profondeur de vos listes.

8
Chris Villa

La méthode TestCase.assertEqual() appelle la classe 'assertDictEqual() pour dicts, il suffit donc de la remplacer dans votre dérivation de sous-classe. Si vous utilisez uniquement d'autres méthodes assertXXX dans la méthode, les messages d'erreur devraient être presque aussi agréables que les assertions intégrées - mais sinon, vous pouvez fournir un argument de mot clé msg lorsque vous appelez pour contrôler ce qui est affiché.

import collections
import unittest

class TestSOquestion(unittest.TestCase):

    def setUp(self):
        pass # whatever...

    def assertDictEqual(self, d1, d2, msg=None): # assertEqual uses for dicts
        for k,v1 in d1.iteritems():
            self.assertIn(k, d2, msg)
            v2 = d2[k]
            if(isinstance(v1, collections.Iterable) and
               not isinstance(v1, basestring)):
                self.assertItemsEqual(v1, v2, msg)
            else:
                self.assertEqual(v1, v2, msg)
        return True

    def test_stuff(self):
        lst1 = [1, 2]
        lst2 = [2, 1]

        d1 = {'key': lst1}
        d2 = {'key': lst2}

        self.assertItemsEqual(lst1, lst2) # True
        self.assertEqual(d1, d2) # True

if __name__ == '__main__':
    unittest.main()

Production:

> python unittest_test.py
.
---------------------------------------------------------------------->
Ran 1 test in 0.000s

OK

>
4
martineau

J'ai eu le même problème, j'ai dû tester si les champs d'un modèle étaient corrects. Et MyModel._meta.get_all_field_names () renvoie parfois ['a', 'b'] et parfois ['b', 'a'].

Quand je cours:

self.assertEqual(MyModel._meta.get_all_field_names(), ['a', 'b'])

il échoue parfois.

Je l'ai résolu en mettant les deux valeurs dans un set ():

self.assertEqual(set(MyModel._meta.get_all_field_names()), set(['a', 'b'])) #true

self.assertEqual(set(MyModel._meta.get_all_field_names()), set(['b', 'a'])) #true

Cela ne fonctionnera pas (renvoie True) avec:

self.assertEqual(set(['a','a','b','a']), set(['a','b']))  # Also true 

Mais puisque je vérifie les noms de champs d'un modèle, et ceux-ci sont uniques, c'est bon pour moi.

1
Robin van Leeuwen