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).
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.
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
>
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.