如何在python中断言dict包含另一个没有assertDictContainsSubset的字典?

时间:2014-01-11 03:33:40

标签: python python-2.7 python-3.x python-unittest

我知道assertDictContainsSubset可以在python 2.7中执行此操作,但由于某种原因,它在python 3.2中已弃用。那么有没有办法断言一个dict包含另一个没有assertDictContainsSubset

的dict

这似乎不太好:

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

还有其他好方法吗?感谢

8 个答案:

答案 0 :(得分:24)

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

限制:字典值必须是可清除的。

答案 1 :(得分:11)

虽然我正在使用pytest,但我在comment中找到了以下想法。它对我来说真的很棒,所以我觉得它在这里很有用:

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

用于Python 3和

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

for Python 2。

它适用于不可清洗的物品,但您无法准确知道哪个物品最终失败。

答案 2 :(得分:6)

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)

答案 3 :(得分:6)

接受的答案存在的一个大问题是,如果对象值中有不可散列的值,它就不起作用。第二件事是你没有得到有用的输出 - 测试通过或失败,但没有告诉你对象中的哪个字段是不同的。

因此,简单地创建子集字典然后测试它更容易。这样你就可以使用TestCase.assertDictEquals()方法,它会在你的测试运行器中为你提供非常有用的格式化输出,显示实际和期望之间的差异。

我认为最令人愉快和pythonic的方法是使用简单的词典理解:

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)

答案 4 :(得分:1)

即使您在词典中有列表,这里的比较仍然有效:

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)

答案 5 :(得分:1)

这回答了一个比你要问的更广泛的问题,但我在我的测试工具中使用它来查看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

答案 6 :(得分:1)

您可以改为使用assertGreaterEqual()方法。

users = {'id': 28027, 'email': 'chungs.lama@gmail.com','created_at': '2005-02-13'}

data = {"email": "chungs.lama@gmail.com"}

self.assertGreaterEqual(user.items(), data.items())

答案 7 :(得分:0)

在Python 3和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和更低版本中,最好的选择是迭代第一个字典中的键,然后检查第二个字典中的键。

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