2013-08-27 7 views
17

So che, quando si esegue assertEqual su un dizionario, viene chiamato assertDictEqual. Allo stesso modo, assertEqual su una sequenza eseguirà assertSequenceEqual.Come ottenere assertDictEqual con assertSequenceEqual applicato ai valori

Tuttavia, quando assertDictEqual sta confrontando i valori, sembra non utilizzare assertEqual e pertanto non si chiama assertSequenceEqual.

Si consideri il seguente dizionari semplice:

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

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

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

Come posso testare dizionari quali d1 e d2 in modo tale che la loro uguaglianza sia adeguatamente confrontato, applicando ricorsivamente assertEqual -come semantica per i valori?

Desidero evitare l'uso di moduli esterni (come suggerito in this question) se possibile, a meno che non siano estensioni native di django.


EDIT

In sostanza, quello che sono dopo è una costruito nel versione di questo:

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) 

Il problema con il codice di cui sopra è che i messaggi di errore non sono bello come il affermazioni incorporate, e ci sono probabilmente casi limite che ho ignorato (come ho appena scritto in cima alla mia testa).

+2

Con il modulo 'unittest',' self.assertEqual (lst1, lst2) 'non è True ->' AssertionError: Lists differing: [1, 2]! = [2, 1] '. – martineau

+0

@martineau - il mio errore; Ho letto male quella parte della documentazione. Sto cercando un equivalente di 'assertItemsEqual' piuttosto che 'assertSequenceEqual' – sapi

+1

Bene, se rendi' lst1' e 'lst2' lo stesso così il primo' assertEqual' riesce, allora anche il secondo lo farà. – martineau

risposta

4

Il metodo TestCase.assertEqual() chiama la classe 'assertDictEqual() per dicts, quindi basta sovrascrivere quello nella derivazione della sottoclasse. Se si utilizzano solo altri metodi assertXXX nel metodo, i messaggi di errore dovrebbero essere quasi altrettanto validi di quelli predefiniti - ma in caso contrario è possibile fornire un argomento di parola msg quando li si chiama per controllare ciò che viene visualizzato.

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

uscita:

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

OK 

> 
+1

Non posso garantire l'ordine degli elenchi. I test sono per un framework django e non posso fare affidamento sull'ordine in cui le query del database sono le stesse tra il set di test e il risultato previsto. Tutto quello che mi interessa è che l'API mi sta dando i valori corretti in * qualche * ordine. – sapi

+0

OK, ma ancora non capisco perché/come ti aspetti che il primo 'assertEqual (lst1, lst2)' sia True nel tuo codice. – martineau

+0

Questa soluzione non funziona su dicts + elenchi profondamente nidificati. La soluzione basata sull'ordinamento funziona. – Federico

7

e non viene sostituito assertDictEqual, perché non lo fai in modo ricorsivo sorta tuoi dicts prima?

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 

Poi sorta, e l'uso normale assertDictEqual:

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

    self.assertDictEqual(dict1, dict2) 

Questo approccio ha il vantaggio di non preoccuparsi di quanti livelli profondi vostre liste sono.

+2

'Eccezione: '<' non è supportato tra istanze di 'dict' e 'dict'' – tdc

1

Ho avuto lo stesso problema, ho dovuto verificare se i campi di un modello erano corretti. E MyModel._meta.get_all_field_names() a volte restituisce ['a', 'b'] e talvolta ['b', 'a'].

quando corro:

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

a volte fallisce.

ho risolto mettendo entrambi i valori in un set():

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 

questo non funzionerà (restituisce True) con:

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

Ma dal momento che sto controllando per i nomi dei campi di un modello, e quelli sono unici, questo è buono per me.

+0

Si può usare la notazione impostata per migliorare questo, invece di' set ([' a ',' b ']) 'prova' {' a', 'b'} '. Inoltre, c'è una funzione 'self.assertSetEqual ({'a', 'a', 'b', 'a'}, {'a', 'b'})' che controllerà la differenza tra i due set e fallire se non contengono esattamente gli stessi elementi. – Mouscellaneous

Problemi correlati