20

我知道,在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 ><

如何通过递归地将-like 语义应用于值来测试诸如此类d1的字典以d2正确比较它们的相等性?assertEqual

如果可能的话,我想避免使用外部模块(如this question中所建议的那样),除非它们是本机 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)

上面代码的问题是错误消息不如内置断言那么好,而且我可能忽略了一些边缘情况(因为我只是在脑海中写下了这个)。

4

3 回答 3

8

与其重写 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)

这种方法的好处是不关心列表的深度。

于 2015-01-14T18:03:37.127 回答
5

TestCase.assertEqual()方法调用类' assertDictEqual()for 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

>
于 2013-08-27T15:02:40.483 回答
1

我有同样的问题,我必须测试模型的字段是否正确。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 

但是由于我正在检查模型的字段名称,并且这些名称是独一无二的,所以这对我来说很好。

于 2015-08-17T12:18:51.637 回答