Test delle unità su livelli più alti di architettura

1

tl; dr: le librerie e le classi che uso come client sono ben testate. Come posso usare quella conoscenza per ridurre la quantità di test?

Elaborazione: supponiamo per scopi teorici che stiamo implementando una funzione che fa qualcosa di abbastanza semplice, ad esempio, creando una matrice di sin (x) di numeri interi consecutivi. (Quello è Ruby, ma la scelta della lingua era arbitraria)

def arr_of_sins(num)
  Array.new(num){|i| Math.sin(i) }
end

Abbiamo qui l'uso di 2 metodi ben testati, di cui siamo perfettamente certi: Array.new e Math.sin.

Domanda: come testerebbe il nostro #arr_of_sins per minimizzare il test delle dipendenze, ma per essere ancora abbastanza sicuro da usarlo da qualche altra parte?

Domanda bonus: quali test di coloro che hai risposto rispondendo alla domanda precedente che tu (come sviluppatori) avresti lasciato come automatici?

    
posta Nikolay Rys 15.05.2015 - 15:23
fonte

5 risposte

3

Vorrei fare un test unitario che fornisca un input e controlli che l'output sia corretto. Forse 1-3 altri per l'input cattivo (negativo, nullo, zero). E li lascerei tutti automatizzati poiché questo tipo di questo è fatto su misura per i test di unità.

Non mi interessa particolarmente (in questo caso) che ripeto sin e Array.new . Per prima cosa, non dovrei sapere / preoccuparmi dei dettagli di implementazione dell'unità di lavoro. Per un altro, tutto si basa su qualcos'altro; escludere tutte le dipendenze ti mette in una follia.

    
risposta data 15.05.2015 - 15:36
fonte
3

Vorrei testarlo come una scatola nera. Ciò significa che testerei solo l'"interfaccia" di una funzione, come se non avessi mai visto la sua implementazione, che considero un argomento da modificare . Per un dato esempio, posso verificare:

  • Lunghezza array risultante
  • ... limiti (presumo che siano 0..num-1 )
  • Il comportamento in caso di argomento è zero (array vuoto come risultato)
  • ... è negativo
  • ... è null
  • Se i contenuti sono effettivamente risultati di Math.sin , e non qualche altra funzione casuale
  • Tipi di argomento / risultato, se è parte significativa dell'API o altrimenti "esposto"

Alcuni di questi test possono sicuramente sovrapporsi con il test della libreria standard (ad esempio il controllo dei limiti), ma se qualcuno dovesse cambiare l'implementazione in un secondo momento, i test mostrerebbero la differenza, impedendo regression , che è una proprietà desiderata di una suite di test.

    
risposta data 15.05.2015 - 15:55
fonte
1

per scenari semplici

Nel tuo esempio di arr_of_sins , ha due dipendenze: Math.sin e Array.new , tuttavia, entrambi questi sono usati staticamente e quindi per essere sicuri dei tuoi test devi trattarli come hai scritto Math.sin e Array.new te stesso nella tua classe di test.

Questo sembra del tutto ridicolo, ma questo è il problema delle dipendenze statiche che non puoi determinare in che modo la tua logica li usa.

Per il tuo caso particolare, sin può essere tranquillamente e facilmente utilizzato in qualsiasi contesto, quindi per il tuo test potresti chiamare sin stesso per vedere se il risultato della tua classe corrisponde al risultato previsto, tuttavia, tu presto scoprirai che il tuo test unitario sarà esattamente come il tuo codice di produzione, il che è inutile.

Vorrei seguire la risposta di @ Telastyn e fare i casi per ottenere una buona approssimazione della gamma di output che puoi aspettarti.

per scenari complessi

Il problema sorge quando si entra in scenari più complessi in cui si sta testando il codice che si basa su altri sistemi / sottosistemi. È qui che modelli come l'iniezione di dipendenza e il mockismo possono aiutare a rendere i test dell'unità robusti.

Ad esempio, se stai testando un codice che si basa sull'acquisizione di dati da un altro sistema, supponiamo di dover creare un report in formato CSV da una query su un database.

Se un formattatore CSV e un repository dipendono dal tuo generatore di report, allora puoi prendere in giro entrambe le dipendenze e tutto quello che finisci per testare è che la tua classe usa le dipendenze in un modo che ti aspetti e non che vanno nel database per eseguire query xyz.

per riepilogare

Si scrivono test unitari per garantire che il contenuto dei metodi siano corretti, se si presume che sin si comporti come previsto, va comunque bene, ma è comunque necessario assicurarsi che l'unità che si sta testando stia effettivamente chiamando sin , e nell'esempio sopra, l'unico modo per farlo è verificare l'output.

Questo va bene per le unità più piccole dove le dipendenze statiche non hanno dipendenze proprie, ma per casi più complessi è necessario avere una strategia diversa per garantire che l'unità sia corretta.

    
risposta data 15.05.2015 - 15:54
fonte
1

Chiediti quale sia lo scopo di questa funzione. Scrivi i tuoi test basandoti su quello.

Il fatto che stai utilizzando una libreria ben testata è un dettaglio di implementazione.

Se decidi di ridurre il numero di test perché i dettagli dell'implementazione ti consentono di farlo, devi assicurarti che ci sia un test che ti avviserà dell'implementazione cambiando in modo tale che la tua decisione di ridurre il numero di test non è più appropriato e dovrebbe essere rivalutato.

In altre parole: sono con @Telastyn su questo.

Se vuoi davvero ridurre il numero di test dato che stai chiamando una libreria di cui ti fidi, scegli un paio di test essenziali da tenere come parte del tuo automatizzato set di test. In aggiunta a ciò aggiungerei uno o più test per mostrare che viene utilizzata la libreria corretta.

    
risposta data 15.05.2015 - 16:26
fonte
1

Dici che la funzione sin è ben collaudata e ti fidi di essa. Probabilmente hai ragione che è ben testato. Quindi sin (0) è 0, sin (90) = 1, sin (180) = 0, sin (270) = -1 e così via, giusto? Vedete, anche se la funzione fa al 100% correttamente ciò che dovrebbe fare, non significa che faccia ciò che pensate dovrebbe fare.

Ma nel tuo test unitario, ti importa se la matrice è nuova e la funzione seno funziona? Non lo fai, non nel tuo test unitario. Quello che ti interessa è che la funzione che hai scritto restituisce i giusti valori, ed è quello che dovresti testare. Cosa guadagni avendo un test unitario per la funzione seno? Se l'unità verifica il funzionamento della tua funzione, il test dell'unità funzione seno non ti guadagna nulla.

Se il test dell'unità per la tua funzione si interrompe, devi decidere se il tuo codice è sbagliato o se il seno o il codice dell'array è cattivo. Quindi non avrai un collega che ti dirà "indovina un po ', la funzione seno è rotta" e poi dici "bene, questo spiega perché il mio test dell'unità fallisce". Ma ciò non accadrebbe comunque, perché la funzione seno non è rotta. Quindi, solo buon vecchio debugging.

    
risposta data 15.05.2015 - 19:05
fonte

Leggi altre domande sui tag