Come testare il codice che dipende da API complesse (ad esempio Amazon S3)?


13

Sto lottando con il test di un metodo che carica documenti su Amazon S3, ma penso che questa domanda si applichi a qualsiasi API non banale / dipendenza esterna. Ho escogitato solo tre potenziali soluzioni, ma nessuna sembra soddisfacente:

  1. Esegui il codice, carica effettivamente il documento, verifica con l'API di AWS che è stato caricato ed eliminalo alla fine del test. Ciò renderà il test molto lento, costerà denaro ogni volta che viene eseguito il test e non restituirà sempre lo stesso risultato.

  2. Mock S3. Questo è super peloso perché non ho idea degli interni di quell'oggetto e sembra sbagliato perché è troppo complicato.

  3. Assicurati solo che MyObject.upload () sia chiamato con gli argomenti giusti e confida che sto usando correttamente l'oggetto S3. Questo mi disturba perché non c'è modo di sapere con certezza che ho usato correttamente l'API S3 dai soli test.

Ho controllato come Amazon testa il proprio SDK e fanno beffe di tutto. Hanno un aiutante di 200 linee che fa beffe. Non penso sia pratico per me fare lo stesso.

Come lo risolvo?


Non una risposta, ma in pratica usiamo i tre approcci che hai esposto.
jlhonora,

Risposte:


28

Ci sono due problemi che dobbiamo esaminare qui.

Il primo è che sembri guardare tutti i tuoi test dal punto di vista del test unitario. I test unitari sono estremamente preziosi, ma non sono gli unici tipi di test. I test possono in realtà essere suddivisi in diversi livelli, da test di unità molto veloci a test di integrazione meno veloci a test di accettazione ancora più lenti . (Possono esserci ancora più livelli suddivisi, come i test funzionali .)

Il secondo è che stai mescolando le chiamate al codice di terze parti con la tua logica aziendale, creando sfide di test e forse rendendo il tuo codice più fragile.

I test unitari dovrebbero essere veloci e dovrebbero essere eseguiti spesso. Deridere le dipendenze aiuta a mantenere veloci questi test, ma può potenzialmente creare buchi nella copertura se la dipendenza cambia e la derisione no. Il codice potrebbe essere rotto mentre i test sono ancora verdi. Alcune librerie beffardo ti avviseranno se l'interfaccia della dipendenza cambia, altre no.

I test di integrazione, d'altra parte, sono progettati per testare le interazioni tra i componenti, comprese le librerie di terze parti. I mock non dovrebbero essere usati a questo livello di test perché vogliamo vedere come l'oggetto reale interagisce insieme. Poiché stiamo usando oggetti reali, questi test saranno più lenti e non li eseguiremo con la stessa frequenza dei nostri test unitari.

I test di accettazione guardano a un livello ancora più elevato, verificando che i requisiti per il software siano soddisfatti. Questi test vengono eseguiti su tutto il sistema completo che verrebbe distribuito. Ancora una volta, non dovrebbe essere usato il beffardo.

Una linea guida che le persone hanno trovato preziosa per quanto riguarda le beffe è di non deridere i tipi che non possiedi . Amazon possiede l'API per S3 in modo che possano assicurarsi che non cambi sotto di loro. D'altra parte, non hai queste assicurazioni. Pertanto, se si deride l'API S3 nei test, potrebbe cambiare e rompere il codice, mentre i test tutti diventano verdi. Quindi, come possiamo testare l'unità di codice che utilizza librerie di terze parti?

Bene, non lo facciamo. Se seguiamo le linee guida, non possiamo prendere in giro oggetti che non possediamo. Ma ... se possediamo le nostre dipendenze dirette, possiamo deriderle. Ma come? Creiamo il nostro wrapper per l'API S3. Possiamo renderlo molto simile all'API S3 o possiamo adattarlo alle nostre esigenze più da vicino (preferito). Possiamo persino renderlo un po 'più astratto, diciamo un PersistenceServicepiuttosto che un AmazonS3Bucket. PersistenceServicesarebbe un'interfaccia con metodi come #save(Thing)e #fetch(ThingId), i tipi di metodi che potremmo vedere (questi sono esempi, potresti effettivamente desiderare metodi diversi). Ora possiamo implementare un PersistenceServiceintorno all'API S3 (diciamo a S3PersistenceService), incapsulandolo lontano dal nostro codice chiamante.

Ora al codice che chiama l'API S3. Dobbiamo sostituire quelle chiamate con chiamate a un PersistenceServiceoggetto. Usiamo l' iniezione di dipendenza per passare il nostro PersistenceServicenell'oggetto. È importante non chiedere un S3PersistenceService, ma chiedere un PersistenceService. Questo ci consente di scambiare l'implementazione durante i nostri test.

Tutto il codice che utilizzava direttamente l'API S3 ora utilizza il nostro PersistenceServicee il nostro S3PersistenceServiceora effettua tutte le chiamate all'API S3. Nei nostri test, possiamo deridere PersistenceService, dal momento che lo possediamo, e usare il mock per assicurarci che il nostro codice effettui le chiamate corrette. Ma ora questo lascia come testare S3PersistenceService. Ha lo stesso problema di prima: non possiamo testarlo unitamente senza chiamare il servizio esterno. Quindi ... non lo testiamo. Abbiamo potuto deridere le dipendenze S3 API, ma questo ci darebbe poco a sfiducia supplementare. Invece, dobbiamo testarlo a un livello superiore: test di integrazione.

Questo può sembrare un po 'preoccupante dire che non dovremmo testare una parte del nostro codice, ma diamo un'occhiata a ciò che abbiamo realizzato. Avevamo un sacco di codice in tutto il luogo in cui non potevamo test unitari che ora possono essere testati attraverso il PersistenceService. Il nostro disordine di libreria di terze parti è limitato a una singola classe di implementazione. Tale classe dovrebbe fornire le funzionalità necessarie per utilizzare l'API, ma non ha alcuna logica aziendale esterna ad essa collegata. Pertanto, una volta scritto, dovrebbe essere molto stabile e non dovrebbe cambiare molto. Possiamo fare affidamento su test più lenti che non eseguiamo così spesso perché il codice è stabile.

Il prossimo passo è scrivere i test di integrazione per S3PersistenceService. Questi dovrebbero essere separati per nome o cartella in modo che possiamo eseguirli separatamente dai nostri test di unità veloci. I test di integrazione possono spesso utilizzare gli stessi framework di test dei test unitari se il codice è sufficientemente informativo, quindi non è necessario apprendere un nuovo strumento. Il codice effettivo per il test di integrazione è ciò che scriveresti per l'opzione 1.


la domanda è: come si eseguono i test di integrazione o meglio e2e per l'API esposta. Non è possibile deridere PersistenceService per ovvi motivi. O ho capito male qualcosa, o aggiungendo un altro livello tra l'API dell'applicazione e l'API AWS, non ti dà altro che avere più tempo a fare i test unitari
Yerken,

@Yerken Mentre ci sto pensando, sono abbastanza sicuro di poter riempire un'altra lunga risposta a quella domanda. Potrebbe anche valere la pena per te perché potresti ottenere più della mia sola risposta.
cbojar,

4

Devi fare entrambe le cose.

Eseguire, caricare ed eliminare è un test di integrazione. Si interfaccia con un sistema esterno e pertanto ci si può aspettare che funzioni lentamente. Probabilmente non dovrebbe far parte di ogni singola build eseguita localmente, ma dovrebbe essere parte di una build CI o di una build notturna. Ciò compensa la lentezza di quei test e fornisce comunque il valore di averlo testato automaticamente.

Sono inoltre necessari unittest eseguiti più rapidamente. Dato che in genere è intelligente non dipendere troppo da un sistema esterno (quindi è possibile scambiare implementazioni o passare), probabilmente si dovrebbe provare a scrivere una semplice interfaccia su S3 su cui si possa codificare. Derida questa interfaccia in unittest in modo da poter avere unittest a esecuzione rapida.

I primi test controllano che il tuo codice funzioni effettivamente con S3, il secondo verifica che il tuo codice chiami correttamente il codice che comunica con S3.


2

Direi che dipende dalla complessità del tuo utilizzo dell'API .

  1. Devi assolutamente fare almeno alcuni test che invocano effettivamente l'API S3 e confermano che ha funzionato da un capo all'altro.

  2. È inoltre necessario eseguire ulteriori test che in realtà non chiamano l'API, in modo da poter testare adeguatamente il proprio software senza invocare l'API per tutto il tempo.

La domanda che rimane è: devi deridere l'API?

E penso che dipende da quanto ci fai. Se stai solo eseguendo una o due semplici azioni, non penso che tu debba affrontare tutti i problemi di un modello. Sarei soddisfatto solo controllando il mio uso delle funzioni e facendo alcuni test dal vivo.

Tuttavia, se il tuo utilizzo è più complesso, con scenari diversi e variabili diverse che potrebbero influenzare i risultati, probabilmente dovrai prenderlo in giro per fare test più approfonditi.


1

Aggiungendo alle risposte precedenti, la domanda principale è se (e come) si desidera deridere l'API S3 per i test.

Invece di deridere manualmente le singole risposte S3, puoi trarre vantaggio da alcuni framework di derisione esistenti molto sofisticati. Ad esempio moto fornisce funzionalità molto simili all'API S3 effettiva.

Puoi anche dare un'occhiata a LocalStack , un framework che combina strumenti esistenti e fornisce un ambiente cloud locale completamente funzionale (incluso S3) che facilita i test di integrazione.

Sebbene alcuni di questi strumenti siano scritti in altre lingue (Python), dovrebbe essere facile far girare l'ambiente di test in un processo esterno dai test in, diciamo, Java / JUnit.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.