web-dev-qa-db-ja.com

PythonでassertDictContainsSubsetなしで別のdictを含むdictをアサートするにはどうすればよいですか?

assertDictContainsSubsetがpython 2.7でこれを行うことができることを知っていますが、何らかの理由でpython 3.2で廃止されています。 dictにはassertDictContainsSubsetのない別のものが含まれていますか?

これは良くないようです:

for item in dic2:
    self.assertIn(item, dic)

他の良い方法は?ありがとう

37
JerryCai
>>> d1 = dict(a=1, b=2, c=3, d=4)
>>> d2 = dict(a=1, b=2)
>>> set(d2.items()).issubset( set(d1.items()) )
True

その他の方法:

>>> set(d1.items()).issubset( set(d2.items()) )
False

制限:辞書の値はハッシュ可能でなければなりません。

25
John1024

Pytestを使用していますが、 コメント で次のアイデアを見つけました。それは私にとって本当に素晴らしかったので、ここで役に立つと思いました:

assert dict1.items() <= dict2.items()

for Python 3および

assert dict1.viewitems() <= dict2.viewitems()

for Python 2。

ハッシュ不可のアイテムでも機能しますが、最終的にどのアイテムが失敗するかを正確に知ることはできません。

14
kepler

John1024のソリューションは私のために働いた。ただし、失敗した場合、どのキーが一致していないかを示すのではなく、Falseのみを通知します。そこで、有用な失敗メッセージを出力する他のアサーションメソッドを使用して、非推奨のアサートメソッドを回避しようとしました。

    expected = {}
    response_keys = set(response.data.keys())
    for key in input_dict.keys():
        self.assertIn(key, response_keys)
        expected[key] = response.data[key]
    self.assertDictEqual(input_dict, expected)
6
Risadinha

受け入れられた答えの大きな問題は、オブジェクト値にハッシュできない値が含まれていると機能しないことです。 2つ目は、有用な出力が得られないことです。テストは成功または失敗しますが、オブジェクト内のどのフィールドが異なるかはわかりません。

そのため、サブセット辞書を作成してテストする方が簡単です。この方法では、TestCase.assertDictEquals()メソッドを使用できます。これにより、テストランナーで実際の出力と期待される出力の差分を表示する非常に便利なフォーマット出力が得られます。

私がこれを行うための最も楽しいとPythonの方法は、そのような単純な辞書の理解を使用することだと思います:

_from unittest import TestCase


actual = {}
expected = {}

subset = {k:v for k, v in actual.items() if k in expected}
TestCase().assertDictEqual(subset, expected)
_

注:TestCaseを継承する子クラスに属するメソッドでテストを実行している場合(ほぼ確実にそうであるように)、それはただself.assertDictEqual(subset, expected)です

6
Sam Redway

これは、あなたが尋ねているよりも少し幅広い質問に答えますが、container辞書にcontained辞書のように見えるものが含まれているかどうかを確認するために、テストハーネスでこれを使用します。これにより、キーと値がチェックされます。さらに、キーワード'ANYTHING'は、どのように一致するかを気にしないことを示します。

def contains(container, contained):
    '''ensure that `contained` is present somewhere in `container`

    EXAMPLES:

    contains(
        {'a': 3, 'b': 4},
        {'a': 3}
    ) # True

    contains(
        {'a': [3, 4, 5]},
        {'a': 3},
    ) # True

    contains(
        {'a': 4, 'b': {'a':3}},
        {'a': 3}
    ) # True

    contains(
        {'a': 4, 'b': {'a':3, 'c': 5}},
        {'a': 3, 'c': 5}
    ) # True

    # if an `contained` has a list, then every item from that list must be present
    # in the corresponding `container` list
    contains(
        {'a': [{'b':1}, {'b':2}, {'b':3}], 'c':4},
        {'a': [{'b':1},{'b':2}], 'c':4},
    ) # True

    # You can also use the string literal 'ANYTHING' to match anything
        contains(
        {'a': [{'b':3}]},
        {'a': 'ANYTHING'},
    ) # True

    # You can use 'ANYTHING' as a dict key and it indicates to match the corresponding value anywhere
    # below the current point
    contains(
        {'a': [ {'x':1,'b1':{'b2':{'c':'SOMETHING'}}}]},
        {'a': {'ANYTHING': 'SOMETHING', 'x':1}},
    ) # True

    contains(
        {'a': [ {'x':1, 'b':'SOMETHING'}]},
        {'a': {'ANYTHING': 'SOMETHING', 'x':1}},
    ) # True

    contains(
        {'a': [ {'x':1,'b1':{'b2':{'c':'SOMETHING'}}}]},
        {'a': {'ANYTHING': 'SOMETHING', 'x':1}},
    ) # True
    '''
    ANYTHING = 'ANYTHING'
    if contained == ANYTHING:
        return True

    if container == contained:
        return True

    if isinstance(container, list):
        if not isinstance(contained, list):
            contained = [contained]
        true_count = 0
        for contained_item in contained:
            for item in container:
                if contains(item, contained_item):
                    true_count += 1
                    break
        if true_count == len(contained):
            return True

    if isinstance(contained, dict) and isinstance(container, dict):
        contained_keys = set(contained.keys())
        if ANYTHING in contained_keys:
            contained_keys.remove(ANYTHING)
            if not contains(container, contained[ANYTHING]):
                return False

        container_keys = set(container.keys())
        if len(contained_keys - container_keys) == 0:
            # then all the contained keys are in this container ~ recursive check
            if all(
                contains(container[key], contained[key])
                for key in contained_keys
            ):
                return True

    # well, we're here, so I guess we didn't find a match yet
    if isinstance(container, dict):
        for value in container.values():
            if contains(value, contained):
                return True

    return False
1
JnBrymn

辞書にリストがある場合でも機能する比較を次に示します。

superset = {'a': 1, 'b': 2}
subset = {'a': 1}

common = { key: superset[key] for key in set(superset.keys()).intersection(set(subset.keys())) }

self.assertEquals(common, subset)
1
user1338062

代わりにassertGreaterEqual()メソッドを使用できます。

users = {'id': 28027, 'email': '[email protected]','created_at': '2005-02-13'}

data = {"email": "[email protected]"}

self.assertGreaterEqual(user.items(), data.items())
0
Bomjon Bedu

Python 3 and Python 2.7では、データをコピーせずに辞書のセットのような「アイテムビュー」を作成できます。これにより、比較を使用できます。サブセット関係をテストする演算子。

Python 3では、これは次のようになります。

_# Test if d1 is a sub-dict of d2
d1.items() <= d2.items()

# Get items in d1 not found in d2
difference = d1.items() - d2.items()
_

Python 2.7では、viewitems()の代わりにitems()メソッドを使用して同じ結果を得ることができます。

Python 2.6以前では、最初の辞書のキーを反復処理し、2番目の辞書に含まれているかどうかを確認するのが最善の策です。

_# Test if d1 is a subset of d2
all(k in d2 and d2[k] == d1[k] for k in d1)
_
0
augurar