Unit test di un client API e wrapper


14

Ho girato in tondo cercando di capire il modo migliore per testare l'unità di una libreria client API che sto sviluppando. La libreria ha una Clientclasse che fondamentalmente ha una mappatura 1: 1 con l'API e una Wrapperclasse aggiuntiva che fornisce un'interfaccia più user-friendly sulla parte superiore di Client.

Wrapper --> Client --> External API

Ho scritto per la prima volta un mucchio di test su entrambi Cliente Wrapper, in effetti, ho semplicemente verificato che essi inoltrano alle funzioni appropriate di qualunque cosa operino ( Wrapperopera Cliente Clientopera su una connessione HTTP). Ho iniziato a sentirmi a disagio con questo, tuttavia, perché mi sento come se stessi testando l'implementazione di queste classi, piuttosto che l'interfaccia. In teoria, potrei cambiare le classi per avere un'altra implementazione perfettamente valida, ma i miei test fallirebbero perché le funzioni che mi aspettavo di essere chiamate non venivano chiamate. A me sembrano fragili prove.

Dopo questo, ho pensato all'interfaccia delle classi. I test dovrebbero verificare che le classi facciano effettivamente il lavoro che dovrebbero fare, piuttosto che come lo fanno. Quindi come posso farlo? La prima cosa che viene in mente è il mobbing delle richieste API esterne. Tuttavia, sono nervoso per l'eccessiva semplificazione del servizio esterno. Molti esempi di API testate che ho visto danno solo risposte predefinite, il che sembra un modo davvero semplice per testare solo che il tuo codice sembra funzionare correttamente contro la tua falsa API. L'alternativa è prendere in giro il servizio, che è semplicemente impossibile, e dovrebbe essere tenuto aggiornato ogni volta che il servizio reale cambia - che sembra eccessivo e perdita di tempo.

Infine, ho letto questo da un'altra risposta sui programmatori SE :

Il compito di un client API remoto è di effettuare determinate chiamate, né più né meno. Pertanto, il suo test dovrebbe verificare che emetta tali chiamate, né più né meno.

E ora sono più o meno convinto - durante il test Client, tutto ciò che devo testare è che fa le richieste corrette all'API (Naturalmente, c'è sempre la possibilità che l'API cambierà ma i miei test continuano a passare - ma questo è dove sarebbero utili i test di integrazione). Dal momento che Clientè solo una mappatura 1: 1 con l'API, la mia preoccupazione prima di passare da un'implementazione valida a un'altra non si applica davvero - c'è solo un'implementazione valida per ciascun metodo di Client.

Tuttavia, sono ancora bloccato con la Wrapperclasse. Vedo le seguenti opzioni:

  1. Stub out the Clientclass e solo test che vengono chiamati i metodi appropriati. In questo modo, sto facendo lo stesso come sopra ma sto trattando Clientcome un supporto per l'API. Questo mi riporta al punto di partenza. Ancora una volta, questo mi dà la spiacevole sensazione di testare l'implementazione, non l'interfaccia. Il Wrapperpotrebbe benissimo essere implementato usando un client completamente diverso.

  2. Creo un finto Client. Ora devo decidere fino a che punto andare a deriderlo: creare una derisione completa del servizio richiederebbe molto sforzo (più lavoro di quanto non sia andato nella biblioteca stessa). L'API stessa è semplice, ma il servizio è piuttosto complesso (è essenzialmente un archivio dati con operazioni su tali dati). E ancora, dovrò mantenere il mio finto in sintonia con il reale Client.

  3. Ho appena testato che vengono fatte le richieste HTTP appropriate. Ciò significa che Wrapperchiamerà un Clientoggetto reale per fare quelle richieste HTTP, quindi non lo sto testando da solo. Questo rende un po 'un test unitario terribile.

Quindi non sono particolarmente soddisfatto di nessuna di queste soluzioni. Cosa faresti? C'è un modo giusto per farlo?


Tendo a evitare i test unitari in questi scenari in cui esiste una libreria di terze parti che esegue gran parte del lavoro di routine e ho semplicemente un wrapper (principalmente perché non ho idea di come farlo in un modo che testa qualcosa di veramente significativo). In genere faccio test di integrazione in questi casi, possibilmente con un servizio simulato. Forse qualcuno sa come effettuare un test unitario davvero significativo per questi - tendo a dare la priorità ai componenti più critici del sistema sotto il nostro controllo. Qui la parte critica è fuori dal nostro controllo. :-(

Risposte:


10

TLDR : nonostante la difficoltà, è necessario stub il servizio e l'utilizzo per i test delle unità client.


Non sono sicuro che il "compito di un client API remoto sia di emettere determinate chiamate, né più, né meno ...", a meno che l'API non consista solo di endpoint che restituiscono sempre uno stato fisso e non consumano né producono qualsiasi dato. Questa non sarebbe l'API più utile ...

Dovresti anche verificare che il client non solo invii le richieste corrette, ma che gestisca correttamente il contenuto della risposta, errori, reindirizzamenti, ecc. E test per tutti questi casi.

Come noterai, dovresti avere test di integrazione che coprano l'intero stack da wrapper -> client -> servizio -> DB e oltre, ma per rispondere alla tua domanda principale, a meno che tu non abbia un ambiente in cui i test di integrazione possono essere eseguiti come parte di ogni Build CI senza molti mal di testa (database di test condivisi, ecc.), Dovresti investire il tempo nella creazione di uno stub dell'API.

Lo stub ti consentirà di creare un'implementazione funzionante del servizio, ma senza dover implementare alcun livello al di sotto del servizio stesso.

A tal fine, è possibile utilizzare una soluzione basata su DI, con un'implementazione del modello di repository sotto le risorse REST:

  • Sostituisci tutto il codice funzionale nei gestori REST con le chiamate a un IWhateverRepository.
  • Creare un Repository ProductionWhatever con il codice estratto dalle risorse REST e un Repository TestWhatever che restituisce risposte predefinite da utilizzare durante il test dell'unità.
  • Utilizzare il contenitore DI per iniettare la DLL / classe ProductionWhateverRepository o TestWhateverRepository, ecc. A seconda della configurazione.

Ad ogni modo, a meno che lo stubing e / o il refactoring del servizio non siano fuori questione né politicamente né praticamente, probabilmente intraprenderei qualcosa di simile a quanto sopra. Se non fosse possibile, mi assicurerei di avere una copertura di integrazione davvero buona ed eseguirli tutte le volte che è possibile, dato il setup di test disponibile.

HTH

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.