Как достичь assertDictEqual с помощью assertSequenceEqual, применяемого к значениям
Я знаю, что при выполнении assertEqual
в словаре вызывается assertDictEqual
. Аналогично, assertEqual
в последовательности будет выполнять assertSequenceEqual
.
Однако, когда assertDictEqual
сравнивает значения, кажется, что он не использует assertEqual
, и поэтому assertSequenceEqual
не вызывается.
Рассмотрим следующие простые словари:
lst1 = [1, 2]
lst2 = [2, 1]
d1 = {'key': lst1}
d2 = {'key': lst2}
self.assertEqual(lst1, lst2) # True
self.assertEqual(d1, d2) # False ><
Как я могу тестировать словари, такие как d1
и d2
, чтобы их соответствие правильно сравнивалось, рекурсивно применяя семантику assertEqual
к значениям?
Я хочу избежать использования внешних модулей (как предложено в этом вопросе), если это вообще возможно, если только они не являются родными расширениями django.
ИЗМЕНИТЬ
По существу, для меня это встроенная версия:
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)
Проблема с вышеприведенным кодом заключается в том, что сообщения об ошибках не так хороши, как утверждается встроенным, и, возможно, в некоторых случаях я игнорировал (как я только что написал это с моей головы).
Ответы
Ответ 1
Вместо того, чтобы переопределять assertDictEqual, почему бы вам не рекурсивно не отсортировать свои dicts?
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
Затем сортируйте и используйте обычный assertDictEqual:
dict1 = deep_sort(dict1)
dict2 = deep_sort(dict2)
self.assertDictEqual(dict1, dict2)
Этот подход имеет преимущество: не заботиться о том, сколько уровней в ваших списках.
Ответ 2
Метод TestCase.assertEqual()
вызывает класс assertDictEqual()
для dicts
, поэтому просто переопределяем его в вашем подклассе. Если вы используете только другие методы assertXXX
в методе, сообщения об ошибках должны быть почти такими же хорошими, как и встроенные утверждения, но если вы не можете предоставить аргумент ключевого слова msg
, когда вы вызываете их для управления отображаемым.
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()
Вывод:
> python unittest_test.py
.
---------------------------------------------------------------------->
Ran 1 test in 0.000s
OK
>
Ответ 3
У меня была та же проблема, я должен был проверить, правильно ли поля модели. И MyModel._meta.get_all_field_names() иногда возвращает ['a', 'b'], а иногда ['b', 'a'].
Когда я запустил:
self.assertEqual(MyModel._meta.get_all_field_names(), ['a', 'b'])
он иногда терпит неудачу.
Я решил это, поставив оба значения в 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
Это не сработает (возвращает True) с помощью:
self.assertEqual(set(['a','a','b','a']), set(['a','b'])) # Also true
Но так как я проверяю имена полей модели, и они уникальны, это хорошо для меня.