How to achieve assertDictEqual with assertSequenceEqual applied to values

sapi picture sapi · Aug 27, 2013 · Viewed 28k times · Source

I know that, when performing assertEqual on a dictionary, assertDictEqual is called. Similarly, assertEqual on a sequence will perform assertSequenceEqual.

However, when assertDictEqual is comparing values, it appears not to make use of assertEqual, and thus assertSequenceEqual is not called.

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 d1 and d2 such that their equality is properly compared, by recursively applying assertEqual-like semantics to values?

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


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

Answer

Chris Villa picture Chris Villa · Jan 14, 2015

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

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:

    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.