¿Cómo afirmar que un dict contiene otro dict sin assertDictContainsSubset en python?


Sé que assertDictContainsSubset puede hacer esto en python 2.7, pero por alguna razón está obsoleto en python 3.2. Entonces, ¿hay alguna manera de afirmar que un dict contiene otro sin assertDictContainsSubset?

Esto no parece bueno:

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

¿Alguna otra buena manera? Gracias

Author: laike9m, 2014-01-11

6 answers

>>> d1 = dict(a=1, b=2, c=3, d=4)
>>> d2 = dict(a=1, b=2)
>>> set(d2.items()).issubset( set(d1.items()) )
True

Y al revés:

>>> set(d1.items()).issubset( set(d2.items()) )
False

Limitación: los valores del diccionario tienen que ser hashables.

 22
Author: John1024,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-11 06:14:23

Aunque estoy usando pytest, encontré la siguiente idea en un comentario . Funcionó muy bien para mí, así que pensé que podría ser útil aquí:

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

Para Python 3 y

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

Para Python 2.

Funciona con elementos no hashables, pero no se puede saber exactamente qué elemento finalmente falla.

 6
Author: kepler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-01 17:11:05

La solución de John1024 funcionó para mí. Sin embargo, en caso de un error, solo le indica False en lugar de mostrarle qué claves no coinciden. Por lo tanto, traté de evitar el método assert obsoleto mediante el uso de otros métodos de aserción que generarán mensajes de error útiles:

    expected = {}
    for key in input_dict.keys():
        self.assertIn(key, set(response.data.keys()))
        expected[key] = response.data[key]
    self.assertDictEqual(input_dict, expected)
 3
Author: Risadinha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-17 14:24:10

Aquí hay una comparación que funciona incluso si tiene listas en los diccionarios:

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)
 1
Author: user1338062,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-16 08:48:12

Esto responde a una pregunta un poco más amplia de lo que estás haciendo, pero uso esto en mis arneses de prueba para ver si el diccionario container contiene algo que se parece al diccionario contained. Esto comprueba las claves y los valores. Además, puede usar la palabra clave 'ANYTHING' para indicar que no le importa cómo coincide.

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
 1
Author: JnBrymn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-02 07:59:59

El gran problema con la respuesta aceptada es que no funciona si tiene valores no hashables en los valores de sus objetos. La segunda cosa es que no obtiene una salida útil: la prueba pasa o falla, pero no le dice qué campo dentro del objeto es diferente.

Como tal, es más fácil simplemente crear un diccionario de subconjuntos y luego probarlo. De esta manera, puede usar el método TestCase.assertDictEquals() que le dará una salida formateada muy útil en su corredor de prueba que muestra la diferencia entre y lo esperado.

Creo que la forma más agradable y pitónica de hacer esto es con una comprensión simple del diccionario como tal:

from unittest import TestCase


actual = {}
expected = {}

subset = {k:v for k, v in actual.items() if k in expected}
TestCase().assertDictEqual(subset, expected)

TENGA en cuenta que obviamente si está ejecutando su prueba en un método que pertenece a una clase hija que hereda de TestCase (como casi seguramente debería ser), entonces es simplemente self.assertDictEqual(subset, expected)

 1
Author: Sam Redway,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-24 12:13:42