2013-08-27 17 views
17

Wiem, że podczas wykonywania assertEqual w słowniku jest wywoływana assertDictEqual. Podobnie, assertEqual na sekwencji wykona assertSequenceEqual.Jak osiągnąć wartość assertDictEqual z wartością assertSequenceEqual zastosowaną do wartości

Jednakże, gdy assertDictEqual porównuje wartości, wydaje się nie korzystać z assertEqual, a tym samym assertSequenceEqual nie jest wywoływana.

Rozważmy następujące proste słowniki:

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

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

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

Jak mogę przetestować słowniki takie jak d1 i d2 taki, że ich równość jest prawidłowo porównaniu przez rekursywnie stosowania assertEqual -jak semantykę do wartości?

Chcę uniknąć używania modułów zewnętrznych (zgodnie z sugestią in this question), jeśli w ogóle możliwe, o ile nie są natywnymi rozszerzeniami django.


EDIT

Zasadniczo, co jestem po jest zbudowany w wersji to:

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) 

Problem z powyższego kodu jest to, że komunikaty o błędach nie są tak ładne jak builtin twierdzi, i są prawdopodobnie przypadki skrajne, które zignorowałem (tak jak właśnie to napisałem).

+2

z moduł 'unittest'' self.assertEqual (lst1, lst2) 'jest prawda ->' AssertionError: Tabela różnią [1, 2] = [2, 1] '. – martineau

+0

@martineau - mój błąd; Źle przeczytałem tę część dokumentacji. Szukam odpowiednik 'assertItemsEqual' zamiast' assertSequenceEqual' – sapi

+1

dobrze, jeśli uczynić '' lst2' lst1' i tak samo pierwszy 'assertEqual' powiedzie, wówczas drugi będzie też. – martineau

Odpowiedz

4

Metoda TestCase.assertEqual() wywołuje klasę "assertDictEqual() dla dicts, więc wystarczy przesłonić to w swojej pochodnej podklasy. Jeśli w metodzie będą używane tylko inne metody, komunikaty o błędach powinny być prawie tak samo dobre, jak wbudowane potwierdzenia - ale jeśli nie, możesz podać argument słowa kluczowego msg, gdy wywołasz je, aby kontrolować, co jest wyświetlane.

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

wyjściowa:

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

OK 

> 
+1

Nie mogę zagwarantować kolejności list. Testy dotyczą szkieletu django i nie mogę polegać na tym, że kolejność zapytań do bazy danych jest taka sama między zestawem testowym a oczekiwanym wynikiem. Wszystko, co mnie obchodzi to to, że API podaje mi prawidłowe wartości w * * * kolejności. – sapi

+0

OK, ale nadal nie rozumiem, dlaczego/jak można oczekiwać, że pierwszy 'assertEqual (lst1, lst2)' będzie prawdziwy w kodzie. – martineau

+0

To rozwiązanie nie działa na głęboko zagnieżdżonych dyktowaniach i listach. Rozwiązanie oparte na sortowaniu ma. – Federico

7

Zamiast nadrzędnymi assertDictEqual, dlaczego nie rekurencyjnie posortować dicts pierwszy?

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 

Następnie porządek i używać normalnego assertDictEqual:

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

    self.assertDictEqual(dict1, dict2) 

Podejście to ma tę zaletę, nie dbając o ile poziomów głęboko twoje listy są.

+2

'Wyjątek: '<' nie jest obsługiwany między instancjami 'dict' i 'dict'' – tdc

1

Miałem ten sam problem, musiałem sprawdzić, czy pola modelu są poprawne. A MyModel._meta.get_all_field_names() czasami zwraca ["a", "b"], a czasami ["b", "a"].

Kiedy biegnę:

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

czasami zawodzi.

Rozwiązałem go umieszczając obie wartości w zestawie():

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 

to nie zadziała (zwraca True) z:

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

Ale ponieważ jestem sprawdzanie nazw pól modelu, a są wyjątkowe, to jest dla mnie dobre.

+0

można użyć zestaw notacji poprawić ten, zamiast' set ([' a ' 'b']) '' {spróbować' a ',' b '} '. Istnieje również funkcja 'self.assertSetEqual ({'a', 'a', 'b', 'a'}}, {'a', 'b'})), która sprawdzi różnicę między dwoma zestawami i nie działają, jeśli nie zawierają dokładnie tych samych elementów. – Mouscellaneous

Powiązane problemy