Test di integrazione: test del servizio in servizio

4

Sto cercando dei consigli su come testare le strategie per il servizio di assistenza alla comunicazione.

Ho un servizio (servizio A) che effettua una chiamata a un altro servizio (B) - che è un'API di riposo. Entrambi i servizi sono di mia proprietà.

Ho alcuni test unitari attorno alle chiamate di servizio e mi limito semplicemente a deridere la libreria HTTP in modo che nessuna richiesta venga effettivamente inviata al servizio. Questo funziona bene per i test unitari, ma mi chiedevo se valesse la pena aggiungere alcuni test di integrazione che testano effettivamente le chiamate e le risposte di servizio.

Il problema che vedo è che il servizio B aggiorna un database, quindi qualsiasi test di integrazione nel servizio A dovrà ripristinare tutte le modifiche effettuate chiamando direttamente il DB. Per me questo non sembra ideale dato che ora il servizio A conosce meglio l'implementazione del servizio B di quanto dovrebbe.

Questi test sono preziosi? Quando ho visto questo tipo di test prima sono spesso fragili e si basano su ambienti di sviluppo in buono stato. Ad esempio, se questa fosse una API di terze parti, non avrei test che la chiamassero direttamente.

Posso pensare a due opzioni:

  1. Scrivi i test di integrazione nel servizio A e questi test chiamano il database del servizio B per ripristinare / inserire i dati secondo necessità.

  2. Rimani con i mock e non aggiungere test di integrazione al servizio A. Aggiungi invece alcuni test funzionali al servizio B che testano i vari endpoint di riposo.

Qualche consiglio o idea?

    
posta M.M 16.09.2017 - 22:42
fonte

3 risposte

5

Secondo la mia esperienza, i nostri Test di servizio non devono essere vincolati da dipendenze che l'esecuzione è fuori dal nostro controllo.

Prima di tutto, restringiamo l'ambito dei test. Come indicato nella domanda, il servizio sotto test è A , quindi concentriamoci sulla verifica A , indipendentemente dalla proprietà di B e dal suo stato ( in esecuzione, bacato o no).

Una cosa importante da raggiungere con i test è il determinismo . L'unico modo in cui dobbiamo garantire il corretto comportamento di A in base alle premesse dei requisiti (non) funzionali è l'implementazione di test che riproducono queste premesse.

Un modo per raggiungere il determinismo è implementare mocks e / o stubss come servizio A dipendenze . Noi riprodurremo i casi d'uso di cui abbiamo bisogno attraverso questi. Qualcuno potrebbe pensare che questo non sia un test di integrazione, ma i test di integrazione non sono necessariamente privati da mock e stubbs. Alcune integrazioni potrebbero coinvolgere più componenti che lavorano insieme. In questi casi, è bene essere in grado di isolare ciascuna di queste integrazioni in modo da poter verificare il comportamento del nostro componente in diverse e diverse condizioni di integrazione.

The problem I see is service B updates a database so any integration tests in service A will have to reset any changes they make by calling the DB directly.

La scrittura di Servizio B nel DB è quasi aneddotica. Provando contro un vero servizio B stiamo, indirettamente, testando il servizio B stesso e l'intero ambiente in cui il servizio è in esecuzione!

Il vero pericolo è nelle condizioni indeterminate in cui Service B sta vivendo al momento del test. Queste condizioni, nel peggiore dei casi, causeranno il fallimento dei test Service A . Falliranno a causa di problemi non correlati al codice. Questi errori non ci danno un feedback significativo sullo stato del codice testato.

Scrivere in DB è il minore dei problemi, ci sono molte più cose che potrebbero andare storte. Ad esempio.

  • Il servizio B non ha un ambiente di prova.
  • Il servizio B ha un ambiente di test ma è stata distribuita una nuova versione che include le modifiche di interruzione.
  • Il servizio B è bacato.
  • L'archiviazione dei dati del servizio B è inattiva o temporaneamente non disponibile.
  • Il servizio B risponde con dati corrotti.
  • Il servizio B è in fase di test e i dati cambiano frequentemente.
  • Il servizio B non è più disponibile.

Dovresti chiederti perché i test non deterministici sono pericolosi. Suggerisco di leggere il blog di Fowler su Eliminare il non-determinismo nei test . Dai un'occhiata anche a questa domanda . La risposta di Doc riassume molto bene l'argomento.

Un esempio di test non deterministici sono Test di sfarfallio . I test instabili sono test che alla fine falliscono a causa di circostanze indeterminate, e di conseguenza i nostri test falliscono di tanto in tanto.

A test suite with flaky tests can become victim of what Diana Vaughan calls normalization of deviance - the idea that over time we can become so accustomed to things being wrong that we start to accept them as being normal and not a problem.

-Building Microservices- by Sam Newman

La normalizzazione della devianza è il seme del male.

Mi spiace, sto divagando ...

Any advice or thoughts?

Durante il test delle integrazioni, né i dati né il comportamento del servizio esterno dovrebbero preoccuparti. Almeno non ancora. 1

Ciò che dovrebbe preoccuparti è testare il corretto consumo di interfaccia (API) e la corretta gestione del feedback (gestione degli errori, deserializzazione, mappature, ecc.). In altre parole, il contratto .

Ultimamente, ho iniziato a lavorare con il concetto di Test doppio e Controlli sui contratti con i clienti con risultati molto positivi.

È vero che richiedono ulteriori sforzi per costruire e mantenere questi test. Questo è il nostro caso. Tuttavia, abbiamo ridotto in modo significativo l'edificio, i test e il tempo di implementazione e otteniamo un feedback più rapido e più significativo da CI.

In linea con la suddetta scrittura e la risposta di @ Justin, potresti essere interessato a strumenti come Mountebank .

1: esiste un posto per i test indirizzati a convalidare il comportamento reale dei servizi esterni. Possono essere posizionati fuori dalla conduttura dell'edificio. Potrebbero o potrebbero non essere essenziali per una distribuzione verde. Dipende se puoi o meno eludere i problemi sollevati dal servizio. È quasi una questione politica piuttosto che tecnica.

    
risposta data 17.09.2017 - 21:26
fonte
0

Hai ragione, un test che accede direttamente al livello del database sarà più fragile, tuttavia, a seconda del valore fornito dal test, potrebbe comunque valerne la pena. per esempio. se stai testando una funzionalità importante e fragile in un'app legacy, allora il valore fornito da questo test potrebbe valere il costo di mantenere questo test fragile.

Detto questo, ci sono un paio di approcci alternativi che potresti trovare utili.

  1. Imposta i tuoi servizi / test in modo che le modifiche possano essere ripristinate tramite l'API o che le modifiche non debbano essere ripristinate. Ad esempio, se il test crea un utente, esporre un endpoint che consente di eliminare o disattivare utenti o creare utenti di test con un prefisso che è univoco per ciascuna esecuzione di test e ignorare quelli con un prefisso diverso. Ricorda che la testabilità è una (molto preziosa) funzionalità del software - non dovresti sentire che è in qualche modo controverso introdurre le funzionalità esclusivamente per migliorare la testabilità del tuo software.

  2. Provare contro un server HTTP falso, ad es. un'implementazione fittizia che registra le richieste ricevute e invia risposte appropriate in base al test eseguito. Ciò ha lo svantaggio di non testare l'interazione con il servizio "reale", tuttavia fornisce una copertura che i test unitari non possono testare. In effetti questo tipo di test può fornire una copertura di scenari che possono essere più difficili rispetto al servizio "reale", ad es. test delle risposte agli errori o alta latenza.

risposta data 16.09.2017 - 23:40
fonte
0

I tuoi test di integrazione non dovrebbero coinvolgere direttamente il DB. Bene, la domanda è quale interazione vuoi testare:

+-----+      +-----+      +------+
|  A  |<---->|  B  |<---->|  DB  |
+-----+      +-----+      +------+

Stai provando a testare l'interazione A-B o l'interazione A-B-DB? Se si desidera solo testare il sottosistema A-B e il servizio B è una sorta di astrazione sul database in cui non si presuppone che nessun altro servizio scriva sul DB, non si dovrebbe accedere al DB anche per i propri test.

Il problema più importante è che B non è più libero di modificare il database senza aggiornare anche i test di integrazione A-B: ridenominazione delle tabelle, aggiunta di colonne, modifica della tecnologia DB, ecc.

Il modo più semplice per testare A-B in isolamento è avviare un'istanza A e B separata per il test. Il DB è effettivamente una parte di B, quindi anche tu vuoi iniziare con un nuovo database. A volte è possibile creare un nuovo database per test che sarebbe l'ideale. Per esempio. la creazione di un nuovo database SQLite è semplicissima. Se l'impostazione di un DB è più coinvolgente, puoi tenere un DB in giro per i test di integrazione che verranno ripristinati prima di ogni test.

I test di integrazione sono diversi dai test unitari. Nei test unitari, si desidera eseguire ogni test separatamente. Questo non è fattibile per i test di integrazione perché l'inizializzazione dell'ambiente è spesso molto complessa e richiede molto tempo. Spesso è meglio organizzare i singoli casi di test in suite di test, in cui ogni test case dipende dai risultati del test precedente. L'ambiente è inizializzato solo all'inizio di ogni suite di test. In cambio di test più rapidi paghi con risultati di test meno utili: se un test in anticipo in una suite di test fallisce, i test rimanenti non possono essere eseguiti.

Un modo semplice per aggiungere test di integrazione a un sistema esistente è registrare le interazioni del mondo reale, quindi riprodurle per il test. Per esempio. Una volta ho scritto uno strumento in grado di analizzare l'output del registro di un sistema: per creare un nuovo banco di prova, copiarei l'input & uscita dal file di log, anonimizza i dati e salvali nei file testname.in.txt e testname.out.txt . Il test runner dovrebbe quindi passare attraverso una directory piena di questi file, riprodurre l'input e diffare il risultato con l'output atteso. Tuttavia, devi fare attenzione a selezionare casi di test rappresentativi. Ripetere test simili è una perdita di tempo.

    
risposta data 17.09.2017 - 22:14
fonte

Leggi altre domande sui tag