web-dev-qa-db-ja.com

assertSequenceEqualを値に適用してassertDictEqualを実現する方法

辞書でassertEqualを実行すると、assertDictEqualが呼び出されることを知っています。同様に、シーケンスのassertEqualassertSequenceEqualを実行します。

ただし、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のようなセマンティクスを値に再帰的に適用することにより、d1d2などのディクショナリをテストして、等しいかどうかを適切に比較するにはどうすればよいですか?

ネイティブ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のケースが存在することです(私は頭の中で書きました)。

18
sapi

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)

このアプローチには、リストのレベル数を気にしないという利点があります。

8
Chris Villa

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

>
4
martineau

同じ問題がありました。モデルのフィールドが正しいかどうかをテストする必要がありました。また、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 

しかし、私はモデルのフィールド名をチェックしていて、それらは一意であるため、これは私にとっては良いことです。

1