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 Client
classe che fondamentalmente ha una mappatura 1: 1 con l'API e una Wrapper
classe 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 Client
e Wrapper
, in effetti, ho semplicemente verificato che essi inoltrano alle funzioni appropriate di qualunque cosa operino ( Wrapper
opera Client
e Client
opera 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 Wrapper
classe. Vedo le seguenti opzioni:
Stub out the
Client
class e solo test che vengono chiamati i metodi appropriati. In questo modo, sto facendo lo stesso come sopra ma sto trattandoClient
come 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. IlWrapper
potrebbe benissimo essere implementato usando un client completamente diverso.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 realeClient
.Ho appena testato che vengono fatte le richieste HTTP appropriate. Ciò significa che
Wrapper
chiamerà unClient
oggetto 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?