辞書で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 ><
assertEqual
のようなセマンティクスを値に再帰的に適用することにより、d1
やd2
などのディクショナリをテストして、等しいかどうかを適切に比較するにはどうすればよいですか?
ネイティブ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)
上記のコードの問題は、エラーメッセージが組み込みのアサートほど適切ではなく、おそらく無視したEdgeのケースが存在することです(私は頭の中で書きました)。
AssertDictEqualをオーバーライドするのではなく、まず辞書を再帰的にソートしてみませんか?
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)
このアプローチには、リストのレベル数を気にしないという利点があります。
TestCase.assertEqual()
メソッドは、dicts
のクラスのassertDictEqual()
を呼び出すので、サブクラスの派生でそれをオーバーライドします。メソッドで他の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
>
同じ問題がありました。モデルのフィールドが正しいかどうかをテストする必要がありました。また、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
しかし、私はモデルのフィールド名をチェックしていて、それらは一意であるため、これは私にとっては良いことです。