2012-09-28 15 views
8

Sto avendo difficoltà funzioni Python di test che restituiscono un iterabile, come le funzioni che sono cedere o funzioni che semplicemente restituiscono un iterabile, come return imap(f, some_iter) o return permutations([1,2,3]).Funzioni di test iterable restituendo in python

Quindi, con l'esempio delle permutazioni, mi aspetto che l'uscita della funzione sia [(1, 2, 3), (1, 3, 2), ...]. Quindi, inizio a testare il mio codice.

def perm3(): 
    return permutations([1,2,3]) 

# Lets ignore test framework and such details 
def test_perm3(): 
    assertEqual(perm3(), [(1, 2, 3), (1, 3, 2), ...]) 

Questo non funziona, dal momento che è un perm3() iterabile, non una lista . Quindi possiamo risolvere questo particolare esempio.

def test_perm3(): 
    assertEqual(list(perm3()), [(1, 2, 3), (1, 3, 2), ...]) 

E questo funziona bene. Ma cosa succede se ho nested iterables? Questo è iterables che restituiscono iterabili? Come dire le espressioni product(permutations([1, 2]), permutations([3, 4])). Ora questo è probabilmente non utile ma è chiaro che sarà (una volta srotolato gli iteratori ) qualcosa come [((1, 2), (3, 4)), ((1, 2), (4, 3)), ...]. Tuttavia, non possiamo semplicemente avvolgere lo list intorno al nostro risultato, dato che sarà solo trasformare iterable<blah> in [iterable<blah>, iterable<blah>, ...]. Bene Certo che posso fare map(list, product(...)), ma questo funziona solo per un livello nidificazione di 2.

affermativo, la comunità di testing pitone avere alcuna soluzione per i problemi durante il test iterabili? Naturalmente alcuni iterables non possono essere testati in questo modo , come se si volesse un generatore infinito, ma ancora questo problema dovrebbe essere abbastanza comune per qualcuno che abbia pensato a questo .

risposta

4

Io uso KennyTM's assertRecursiveEq:

import unittest 
import collections 
import itertools 

class TestCase(unittest.TestCase): 
    def assertRecursiveEq(self, first, second, *args, **kwargs): 
     """ 
     https://stackoverflow.com/a/3124155/190597 (KennyTM) 
     """ 
     if (isinstance(first, collections.Iterable) 
      and isinstance(second, collections.Iterable)): 
      for first_, second_ in itertools.izip_longest(
        first, second, fillvalue = object()): 
       self.assertRecursiveEq(first_, second_, *args, **kwargs) 
     else: 
      # If first = np.nan and second = np.nan, I want them to 
      # compare equal. np.isnan raises TypeErrors on some inputs, 
      # so I use `first != first` as a proxy. I avoid dependency on numpy 
      # as a bonus. 
      if not (first != first and second != second): 
       self.assertAlmostEqual(first, second, *args, **kwargs)     

def perm3(): 
    return itertools.permutations([1,2,3]) 

class Test(TestCase): 
    def test_perm3(self): 
     self.assertRecursiveEq(perm3(), 
      [(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]) 

if __name__ == '__main__': 
    import sys 
    sys.argv.insert(1, '--verbose') 
    unittest.main(argv = sys.argv) 
+0

Lo accetterò perché è buono come la risposta di dbw. Diversamente dalla mia risposta puoi mescolare le 'tuple' /' lists'. Questa risposta è anche solo eseguibile in copia-incolla. :) Ma! Sarei più soddisfatto se ci fosse una risposta in cui tu (1) controlli effettivamente che restituisce un iterabile (cambia le permutazioni di ritorno ([1,2,3]) 'alla lista di ritorno (permutazioni ([1,2,3 ])) 'non dovrebbe passare ** e ** (2) il valore atteso annidato dovrebbe avere i tipi corretti.Questo sta cambiando una delle tuple in una lista non dovrebbe passare (cambia' (2, 3, 1) 'a' [2, 3, 1] '). – Tarrasch

+0

Ah. Giusto, suppongo di solito evitare i tipi di test e provare a testare le interfacce. In questo modo i dettagli di implementazione cambiano in qualche modo, pur continuando a produrre gli stessi dati/risultati – dbn

0

Non so di alcun modo standard programmatori Python prova iterables, ma si può semplicemente applicare la vostra idea di map e list in una funzione ricorsiva lavorare per qualsiasi livello di nestedness.

def unroll(item): 
    if "__iter__" in dir(item): 
    return map(unroll, item) 
    else: 
    return item 

Quindi il test funzionerà effettivamente.

def test_product_perms(): 
    got = unroll(product(...)) 
    expected = [[[1, 2], [3, 4]], [[1, 2], [4, 3]], ...] 
    assertEqual(got, expected) 

Tuttavia c'è un difetto con questo come si può vedere. Quando srotoli qualcosa, lo sarà sempre trasformato in un array, questo è auspicabile per i file iterabili, ma lo stesso vale per le tuple, . Pertanto, ho dovuto convertire manualmente le tuple nel risultato previsto in elenchi. Quindi, non è possibile diffrentiare se le uscite sono liste o tuple.

Un altro problema con questo approccio ingenuo è che un test di passaggio non significa che la funzione funzioni. Dì che controlli assertEqual(list(my_fun()), [1, 2, 3]), mentre pensi che possa restituire un iterabile che quando "elencato" è uguale a [1, 2, 3]. Potrebbe non essere stato restituito un iterable come richiesto da , potrebbe aver restituito anche una lista o una tupla!

1

È possibile estendere il suggerimento per includere type (che consentiva di distinguere tra elenchi, tuple, ecc.), In questo modo:

def unroll(item): 
    if "__iter__" in dir(item): 
    return map(unroll, item), type(item) 
    else: 
    return item, type(item) 

Ad esempio:

got = unroll(permutations([1,2])) 
([([(1, <type 'int'>), (2, <type 'int'>)], <type 'tuple'>), ([(2, <type 'int'>), (1, <type 'int'>)], <type 'tuple'>)], <type 'itertools.permutations'>) 
# note the final: <type 'itertools.permutations'> 
expected = [(1, 2), (2, 1)] 
assertEqual(x[0], unroll(expected)) # check underlying 
assertEqual(x[1], type(permutations([])) # check type 

.

Una cosa da ricordare, type è grossolana nel distinguere tra oggetti ad es. <type 'classobj'> ...

+1

Preferirei 'isinstance()' 'over tipo()'. –

+0

@AshwiniChaudhary Stavo pensando mentre ho postato/lasciato il lavoro, c'era un modo migliore :) –

2

1. Se l'ordine dei risultati non importa

Usa unittest.assertItemsEqual(). Questo verifica che gli articoli siano presenti sia in se stessi che in riferimento, ma ignora l'ordine. Questo funziona sul tuo esempio, un esempio profondo annidato. Funziona anche su un esempio di 2 profondi che ho inventato.

2. Se l'ordine dei risultati conta

Vorrei suggerire non mai lanciare i risultati di perm3() a un elenco. Invece, confronta gli elementi direttamente mentre procedi. Ecco una funzione di test che funzionerà per il tuo esempio. Ho aggiunto ad una sottoclasse di unittest.TestCase:

def assertEqualIterables(self, itable1, itable2): 
    for ival1, ival2 in zip(itable1, itable2): 
     if "__iter__" in dir(ival1): 
      self.assertEqualIterables(ival1, ival2) 
     else: 
      self.assertEquals(ival1, ival2) 

utilizzarlo come:

def test_perm3(self): 
    reference = [((1, 2), (3, 4)), ((1, 2), (4, 3)), 
       ((2, 1), (3, 4)), ((2, 1), (4, 3)),] 

    self.assertEqualIterables(perm3(), reference) 
+0

Qualcun altro ha notato che assertItemsEqual ha lavorato su diversi nidi profondi di iteratori? Non mi aspettavo che funzionasse ... Sono pazzo? – dbn