Je sais que assertDictContainsSubset
peut le faire dans Python 2.7, mais pour une raison quelconque, il est déconseillé dans Python 3.2. Alors, y a-t-il moyen d'affirmer qu'un dict en contient un autre sans assertDictContainsSubset
?
Cela ne semble pas bon:
for item in dic2:
self.assertIn(item, dic)
un autre bon moyen? Merci
>>> d1 = dict(a=1, b=2, c=3, d=4)
>>> d2 = dict(a=1, b=2)
>>> set(d2.items()).issubset( set(d1.items()) )
True
Et l'inverse:
>>> set(d1.items()).issubset( set(d2.items()) )
False
Limitation: les valeurs du dictionnaire doivent être hashable.
Bien que j'utilise pytest, j'ai trouvé l'idée suivante dans un commentaire . Cela fonctionnait vraiment bien pour moi, alors j'ai pensé que cela pourrait être utile ici:
assert dict1.items() <= dict2.items()
pour Python 3 et
assert dict1.viewitems() <= dict2.viewitems()
pour Python 2.
Cela fonctionne avec les éléments non-hashable, mais vous ne pouvez pas savoir exactement quel article échoue.
La solution de John1024 a fonctionné pour moi. Cependant, en cas d’échec, il vous indique uniquement False
au lieu de vous indiquer les clés qui ne correspondent pas. J'ai donc essayé d'éviter la méthode d'assertion déconseillée en utilisant d'autres méthodes d'assertion qui produiraient des messages d'échec utiles:
expected = {}
for key in input_dict.keys():
self.assertIn(key, set(response.data.keys()))
expected[key] = response.data[key]
self.assertDictEqual(input_dict, expected)
Le gros problème avec la réponse acceptée est que cela ne fonctionne pas si vous avez des valeurs non hashable dans les valeurs de vos objets. La deuxième chose est que vous n'obtenez aucune sortie utile - le test réussit ou échoue mais ne vous dit pas quel champ de l'objet est différent.
En tant que tel, il est plus facile de créer simplement un dictionnaire de sous-ensemble, puis de le tester. De cette façon, vous pouvez utiliser la méthode TestCase.assertDictEquals()
qui vous donnera une sortie formatée très utile dans votre programme d'exécution de test montrant la différence entre le réel et le prévu.
Je pense que la façon la plus agréable et pythonique de faire cela est avec une compréhension simple du dictionnaire en tant que telle:
from unittest import TestCase
actual = {}
expected = {}
subset = {k:v for k, v in actual.items() if k in expected}
TestCase().assertDictEqual(subset, expected)
NOTE évidemment si vous exécutez votre test dans une méthode qui appartient à une classe enfant qui hérite de TestCase (comme vous devriez le faire presque certainement), alors c'est simplement self.assertDictEqual(subset, expected)
Cela répond à une question un peu plus large que celle que vous posez, mais je l’utilise dans mes harnais de test pour voir si le dictionnaire container
contient quelque chose qui ressemble au dictionnaire contained
. Ceci vérifie les clés et les valeurs. De plus, vous pouvez utiliser le mot-clé 'ANYTHING'
pour indiquer que vous ne vous souciez pas de la correspondance.
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
Voici une comparaison qui fonctionne même si vous avez des listes dans les dictionnaires:
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)
Dans Python 3 et Python 2.7, vous pouvez créer une "vue d'élément" d'un dictée sans copier aucune donnée. Cela vous permet d'utiliser des opérateurs de comparaison pour tester une relation de sous-ensemble.
En Python 3, cela ressemble à:
# Test if d1 is a sub-dict of d2
d1.items() <= d2.items()
# Get items in d1 not found in d2
difference = d1.items() - d2.items()
En Python 2.7, vous pouvez utiliser la méthode viewitems()
à la place de items()
pour obtenir le même résultat.
En Python 2.6 et inférieur, votre meilleur choix est de parcourir les clés du premier dict et de vérifier leur inclusion dans le second.
# Test if d1 is a subset of d2
all(k in d2 and d2[k] == d1[k] for k in d1)