Python 如何将 assertSequenceEqual 应用于值来实现 assertDictEqual

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18464095/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 10:49:50  来源:igfitidea点击:

How to achieve assertDictEqual with assertSequenceEqual applied to values

pythonunit-testingpython-unittest

提问by sapi

I know that, when performing assertEqualon a dictionary, assertDictEqualis called. Similarly, assertEqualon a sequence will perform assertSequenceEqual.

我知道,在assertEqual字典上执行时,会assertDictEqual被调用。同样,assertEqual在一个序列上将执行assertSequenceEqual.

However, when assertDictEqualis comparing values, it appears not to make use of assertEqual, and thus assertSequenceEqualis not called.

但是,在assertDictEqual比较值时,似乎没有使用assertEqual,因此assertSequenceEqual不会被调用。

Consider the following simple dictionaries:

考虑以下简单的字典:

lst1 = [1, 2]
lst2 = [2, 1]

d1 = {'key': lst1}
d2 = {'key': lst2}

self.assertEqual(lst1, lst2) # True
self.assertEqual(d1, d2) # False ><

How can I test dictionaries such as d1and d2such that their equality is properly compared, by recursively applying assertEqual-like semantics to values?

如何通过递归地将类似语义应用于值来测试诸如d1d2这样的字典,以便正确比较它们的相等性assertEqual

I want to avoid using external modules (as suggested in this question) if at all possible, unless they are native django extensions.

如果可能的话,我想避免使用外部模块(如本问题所建议的那样),除非它们是本机 django 扩展。



EDIT

编辑

Essentially, what I am after is a built in version of this:

本质上,我所追求的是一个内置版本:

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)

The problem with the above code is that the error messages are not as nice as the builtin asserts, and there's probably edge cases I've ignored (as I just wrote that off the top of my head).

上面代码的问题在于错误消息不像内置断言那么好,而且可能有一些边缘情况我已经忽略了(因为我只是把它写在我的头上)。

回答by martineau

The TestCase.assertEqual()method calls the class' assertDictEqual()for dicts, so just override that in your subclass derivation. If you only use other assertXXXmethods in the method, the error messages should be almost as nice as the built-in asserts -- but if not you can provide a msgkeyword argument when you call them to control what is displayed.

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

Output:

输出:

> python unittest_test.py
.
---------------------------------------------------------------------->
Ran 1 test in 0.000s

OK

>

回答by Chris Villa

Rather than overriding assertDictEqual, why don't you recursively sort your dicts first?

与其覆盖 assertDictEqual,不如先递归地对 dicts 进行排序?

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

Then sort, and use normal assertDictEqual:

然后排序,并使用正常的 assertDictEqual:

    dict1 = deep_sort(dict1)
    dict2 = deep_sort(dict2)

    self.assertDictEqual(dict1, dict2)

This approach has the benefit of not caring about how many levels deep your lists are.

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

回答by Robin van Leeuwen

I had the same problem, i had to test if the fields of a model where correct. And MyModel._meta.get_all_field_names() sometimes returns ['a','b'] and sometimes ['b','a'].

我遇到了同样的问题,我必须测试模型的字段是否正确。MyModel._meta.get_all_field_names() 有时返回 ['a','b'],有时返回 ['b','a']。

When i run:

当我运行时:

self.assertEqual(MyModel._meta.get_all_field_names(), ['a', 'b'])

it sometimes fails.

它有时会失败。

I solved it by putting both values in a set():

我通过将两个值放在一个 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

This will not work (returns True) with:

这将不起作用(返回 True):

self.assertEqual(set(['a','a','b','a']), set(['a','b']))  # Also true 

But since i'm checking for field names of a model , and those are unique, this is good by me.

但是由于我正在检查模型的字段名称,并且这些名称是唯一的,因此这对我来说很好。