Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python unittest's assertDictContainsSubset recommended alternative [duplicate]

I have some tests in Python written in unittest. I want to check that some of my dictionaries contain at least certain attributes equal to certain values. If there are extra values, that would be fine. assertDictContainsSubset would be perfect, except that it's deprecated. Is there a better thing that I should be using or should I just recursively assert the contents to be equal if they are in the target dictionary?

The docs recommend using addTypeEqualityFunc, but I do want to use the normal assertEqual for dicts in some cases.

like image 408
Alphadelta14 Avatar asked Nov 18 '13 15:11

Alphadelta14


3 Answers

On Python 3.9+, use the dictionary union operator.

Change

assertDictContainsSubset(a, b)

to

assertEqual(b, b | a)

On older versions of Python, change it to

assertEqual(b, {**b, **a})

Note the order of the arguments, assertDictContainsSubset put the "larger" dictionary (b) second and the subset (a) first, but it makes more sense to put the larger dictionary (b) first (which is why assertDictContainsSubset was removed in the first place).

This creates a copy of b then iterates over a, setting any keys to their value in a and then compares that result against the original b. If you can add all the keys/values of a to b and still have the same dictionary, it means a doesn't contain any keys that aren't in b and all the keys it contains have the same values as they do in b, i.e. a is a subset of b.

like image 113
Boris Avatar answered Oct 24 '22 08:10

Boris


If you were testing if dict A is a subset of dict B, I think I would write a function that tries to extract the content of dict A from dict B making a new dict C and then assertEqual(A,C).

def extractDictAFromB(A,B):
    return dict([(k,B[k]) for k in A.keys() if k in B.keys()])

then you could just do

assertEqual(A,extractDictAFromB(A,B))
like image 15
Andrew Robinson Avatar answered Oct 24 '22 08:10

Andrew Robinson


Extending on @bman's answer, exploiting that the comparison operators for set-like objects are overloaded as subset operators, you can use assertGreaterEqual for (arguably) better error messages.

Compare the two tests:

import unittest

class SubsetTestCase(unittest.TestCase):
    def test_dict_1(self):
        a = {1: 1, 2: 2}
        b = {1: 2}
        self.assertTrue(a.items() >= b.items())

    def test_dict_2(self):
        a = {1: 1, 2: 2}
        b = {1: 2}
        self.assertGreaterEqual(a.items(), b.items())

unittest.main()

The result is:

======================================================================
FAIL: test_dict_1 (__main__.SubsetTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test.py", line 9, in test_dict_1
    self.assertTrue(a.items() >= b.items())
AssertionError: False is not true

======================================================================
FAIL: test_dict_2 (__main__.SubsetTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test.py", line 15, in test_dict_2
    self.assertGreaterEqual(a.items(), b.items())
AssertionError: dict_items([(1, 1), (2, 2)]) not greater than or equal to dict_items([(1, 2)])

----------------------------------------------------------------------

With assertGreaterEqual, you can see the contents of the two dictionaries from the error message.

like image 12
Ignatius Avatar answered Oct 24 '22 09:10

Ignatius