Perché utilizzare il database in memoria per i test di integrazione?


18

Sono davvero confuso quando vedo molte implementazioni di database in memoria utilizzate per i test, perché ho anche sentito molto dalle migliori pratiche di test di integrazione che l'ambiente che esegue il test dovrebbe assomigliare il più possibile all'ambiente di produzione, incluso il sistema operativo , libreria, motore di database, ecc.

Cosa mi sto perdendo qui?


L'ho visto molto perché puoi garantire a livello di programmazione la coerenza dei dati e in genere sono ragionevolmente veloci. Soprattutto se il tuo test è un test unitario, vuoi che sia un sistema chiuso. Volete che i vostri test siano completamente inclusi nel test.
Rig

È bello che uno sviluppatore sia in grado di ottenere l'ultimo di tutto il codice ed eseguire i test, senza dover prima impostare un database esterno.
Jason Evans,

1
Gli aspetti che richiedono una stretta somiglianza con l'ambiente di produzione appartengono ai test di carico e alle prove di stress, che dovrebbero essere eseguiti separatamente dai test unitari (che come codice impegna criteri di gate) secondo me.
rwong

Risposte:


19

In una tipica situazione di sviluppo software, i test vengono utilizzati in due punti: durante lo sviluppo e prima di spostare il prodotto lungo la catena di sviluppo.

La prima situazione, eseguendo i test durante lo sviluppo, serve obiettivi a breve termine: definire attività (come nel TDD: scrivere un test fallito, quindi superarlo), prevenire le regressioni, assicurarsi che le modifiche non rompano nient'altro, ecc. i test devono essere estremamente veloci: idealmente, l'intera suite di test viene eseguita in meno di 5 secondi e puoi semplicemente eseguirla in un ciclo accanto al tuo IDE o al tuo editor di testo mentre scrivi il codice. Qualsiasi regressione introdotta comparirà in pochi secondi. In questa fase i test rapidi sono più importanti della cattura del 100% di regressioni e bug e poiché non è pratico (o assolutamente impossibile) sviluppare su copie esatte dei sistemi di produzione, lo sforzo richiesto per ottenere test perfetti qui non vale esso. L'uso di database in memoria è un compromesso: non sono copie esatte del sistema di produzione, ma aiutano a mantenere le corse di prova al di sotto del limite di 5 secondi; se la scelta è tra un'impostazione del database leggermente diversa per i miei test relativi al database e nessun test, so cosa scelgo.

La seconda situazione, spostare il codice lungo la catena di sviluppo, tuttavia, non richiede numerosi test. Dal momento che possiamo (e dovremmo) automatizzare questa parte del processo di sviluppo, possiamo permetterci test molto più lenti - anche se un test completo richiede ore, la pianificazione di una build notturna significa comunque avere sempre un quadro preciso della base di codice di ieri. Simulare l'ambiente di produzione nel modo più accurato possibile è importante ora, ma possiamo permettercelo. Quindi non facciamo il compromesso del database in memoria: installiamo la stessa identica versione dello stesso DBMS dei sistemi di produzione e, se possibile, lo riempiamo con i dati di produzione effettivi prima che inizi il test.


6

Immagino sia una velocità di compromesso / corrispondente all'ambiente. I test dovrebbero essere eseguiti spesso e ciò significa che devono essere veloci. Soprattutto test unitari, che non dovrebbero richiedere più di qualche secondo.

I test di integrazione verranno eseguiti più lentamente, ma quando sono veloci è possibile eseguirli più spesso. Ad esempio, prima di ogni commit. Naturalmente, non è completo come un ambiente completo, ma almeno stai testando il livello di mappatura, l'SQL generato, il modo in cui le parti parlano tra loro, ecc. Nel caso di database costosi ti assicuri anche di non " Non è necessario acquistare una licenza per tutti. Potresti riscontrare più errori che coprono il 90% del codice con i test eseguiti una volta all'ora rispetto al 100% del test del codice una volta al giorno o peggio, settimana.

Detto questo, ovviamente è necessario eseguire un test con il database reale e un ambiente completamente integrato. Potresti non eseguire quei test così spesso, ma dal momento che il tuo test precedente ti ha già dato fiducia, tutto ciò che rimane è uno strano bug specifico della piattaforma.


3

Per fare semplici test, deridere il livello di accesso al database è perfettamente accettabile. Chiama getName(), chiama il DAO che è stato deriso e restituisce "John" per il nome e "Smith" per il cognome, li assembla e tutto è perfetto. Non è necessario testare effettivamente un database lì.

Le cose diventano un po 'più quando la logica diventa un po' più complessa. E se avessi un metodo "createOrUpdateUser (...)". Se hai deriso il database, puoi verificare che un determinato metodo sia stato chiamato una volta con un determinato parametro quando il mock non restituisce oggetti e un metodo diverso viene invocato sul database quando restituisce un oggetto esistente. Questo inizia ad arrivare a quella linea fuzzy dove potrebbe essere più facile (specialmente se fosse già lì) creare un database specializzato in memoria e testare quel codice con dati preconfigurati.

In alcuni codici reali su cui ho lavorato (punto vendita), avevamo un resumeSuspededTransaction(...)metodo. Ciò estrarrebbe la transazione dal database in un oggetto (e i suoi componenti) e aggiornerebbe il database. Lo abbiamo deriso e un bug si nascondeva nel codice da qualche parte con la serializzazione e la deserializzazione dei dati che andavano al database (abbiamo cambiato un tipo che era serializzato in modo diverso sul database).

La simulazione non ci ha mostrato il bug perché stava restituendo il suo percorso felice: serializza la transazione, memorizzala nella derisione, deserializzala dalla derisione, verifica che siano uguali. Tuttavia, quando si serializzava un oggetto con uno zero iniziale nel database, li stava rilasciando e quindi ricombinandolo in una stringa senza gli zeri. Abbiamo rilevato il bug senza il database durante la risoluzione dei problemi (non è stato così difficile individuarlo una volta saputo che era lì).

Successivamente, abbiamo inserito un database e ci siamo resi conto che il bug non avrebbe mai superato quel test junit se invece fossimo andati in un database in memoria.


In database di memoria hanno i vantaggi:

  • possono essere ruotati rapidamente (senza bisogno di un DBA per configurare account, tabelle e simili) per i test
  • i dati possono essere preconfigurati per quel test
  • il test non deve preoccuparsi di eseguire il rollback del test al termine
  • ogni test ha il suo nel database di memoria, quindi non devi preoccuparti se due test sono in esecuzione contemporaneamente
  • possono essere eseguiti su sistemi che non hanno connettività ai database reali

1

Questo dipende molto dal sistema di database che stai utilizzando. Quando il tuo sistema db ti offre un'alternativa in memoria che è quasi al 100% API e comportamento compatibile con una configurazione di database basata su disco (tranne per la velocità e la sicurezza, o ovviamente), allora l'uso della variante in memoria va ovviamente bene .

Se, tuttavia, il tuo sistema DB presenta differenze significative tra la configurazione in memoria e l'utilizzo non in memoria, hai ragione: in questo caso i test di integrazione hanno un rischio maggiore di oscurare un bug. Ma anche allora, potresti essere in grado di "astrarre quelle differenze" da solo, dato che conosci bene il tuo sistema DB e le differenze.


1

Nelle parole di laici:

La derisione di parti importanti dell'architettura è OK (e un must) per i test unitari .

Ma per i test di integrazione, sono fortemente d'accordo con te. La derisione non dovrebbe essere fatta e dovrebbe essere fornito un ambiente il più simile possibile a quello reale.

Dopotutto, i test di integrazione riguardano il test di come le diverse parti dell'architettura si comportano insieme.

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.