Database e test di unità / integrazione


25

Ho avuto una discussione con qualcuno sui test di unità / integrazione con applicazioni web e non sono d'accordo su 1 idea di base. Il problema è che la persona con cui sto parlando pensa che il database su cui lavora il test unitario dovrebbe contenere dati precompilati e penso che dovrebbe essere completamente vuoto prima e dopo l'esecuzione dei test.

La mia preoccupazione per i dati precompilati nel database è che non c'è modo di assicurarsi che i dati siano mantenuti in buono stato. I test stessi stanno creando, eliminando e modificando i dati nel database, quindi non vedo davvero come avere dati nel database prima di iniziare i test sia una buona cosa.

Sembra che il modo migliore per testare la funzionalità del database sia avere le seguenti configurazioni:

  1. In una fase di "installazione" prima dell'esecuzione effettiva del test, è innanzitutto necessario troncare tutte le tabelle nel database
  2. Quindi si inseriscono tutti i dati necessari per i casi di test che si sta per eseguire
  3. Quindi esegui e convalidi i casi di test
  4. Quindi in una fase di "smantellamento" si troncano nuovamente tutte le tabelle nel database

Non vedo nessun altro modo migliore per garantire che i dati su cui stai testando siano un buon test verificabile.

Mi sto perdendo qualcosa qui? Non è questo il modo migliore per testare la funzionalità relativa al database? C'è qualche vantaggio ad avere un database precompilato che esiste sempre nel database (anche prima di iniziare i test o dopo che i test sono stati eseguiti)? Anche qualsiasi aiuto nelle idee per spiegare il mio processo in modo diverso per far capire meglio il mio punto sarebbe fantastico (cioè se il mio punto ha dei meriti).


Risposte:


21

Per me i test unitari non dovrebbero occuparsi del database, i test di integrazione riguardano il database.

I test di integrazione che si occupano del database dovrebbero in pratica avere un database vuoto con un approccio di strappo e abbattimento, l'utilizzo di un approccio basato sulle transazioni è un buon modo per procedere (ovvero creare una transazione sull'impostazione e il rollback sull'abbattimento).

Quello che il tuo amico sembra voler fare è testare dal punto di vista della "regressione", cioè avere dati reali lì e vedere come reagisce il sistema, dopo tutto nessun sistema è perfetto e di solito possono esserci dati cattivi in ​​giro da qualche parte che forniscono alcune stranezze al tuo modello di dominio.

Le tue migliori pratiche sono la strada da percorrere e quello che tendo a fare è se trovo uno scenario per dati errati, scrivo un test di integrazione con un setup installato e lo strappo con quello scenario esatto.


Sono solo sicuro che qual è la differenza tra test unitari e test di integrazione oltre a sentire l'unità dovrebbe usare dati derisi e l'integrazione dovrebbe usare un database (avviato un altro thread programmers.stackexchange.com/questions/101300/… per capire la differenza ). A parte questo, tutto ciò che stai dicendo sembra essere in linea con quello che sto pensando.
Ryanzec,

Nessun problema, ho aggiunto ulteriori informazioni alla tua altra risposta
Nicholas Mayne,

1
perché non riesci a provare l'unità DB? Se si inserisce l'SQL nelle procedure memorizzate, è possibile testarle unitamente con dati definiti dal test e improvvisamente tutto è facile. Questa è sicuramente una best practice che più persone dovrebbero seguire, vedi cosa dice la SM
gbjbaanb

1
integration tests- cosa intendi? Come ho già detto, i moduli che utilizzano il database possono e devono essere testati con unit test. Il database può essere deriso manualmente o sostituito con un'implementazione in memoria
hellboy,

6

Se i tuoi test dipendono dal database, penso che sia più importante che i dati che ti interessano siano in uno stato noto per i tuoi test, piuttosto che il database sia vuoto. Una delle misure di buoni test è che ogni test dovrebbe fallire per un motivo e nessun altro test dovrebbe fallire per quello stesso motivo.

Quindi, se il tuo test si preoccupa dello stato dei dati, porta i dati in quello stato noto e riporta i dati a quello stato dopo che i test sono stati eseguiti, in modo che i test siano riproducibili.

Se riesci a separare i tuoi test dallo stato dei dati deridendo, sarebbe anche una buona cosa. Dici che stai facendo test di unità / integrazione, ma ovviamente queste due cose dovrebbero essere considerate separatamente. Se possibile, i test unitari devono essere disaccoppiati dal database e i test di integrazione devono essere testati con il database in uno stato noto.


2

Bene, vedo un vantaggio nell'avere un database prepopolato: non è necessario scrivere il codice che inserirà i dati necessari, poiché è lì. Altrimenti ci sono solo degli svantaggi. Forse qualcuno ha modificato i dati del test sul database? Forse qualcuno ha tentato di aggiornare i dati? Ma la cosa peggiore è avere un caso di test che rovina gravemente il database ... Alla fine si ricrea ricreare l'intero database manualmente più volte.

Hai ragione su come scrivere i test, tranne per il fatto che non troncerei nulla:

  • fase di installazione: ottenere una connessione al database e inserire i dati
  • fase di esecuzione
  • fase di abbattimento: rimuovere i dati inseriti (troncare)

Ora, quello scenario è ottimo per i test unitari. Quando sono necessari dati sia per i test di unità che di integrazione, ho scoperto che anche una grande fase di installazione comune a tutti i casi di test (abbiamo raggruppato tutti gli "inserti" in un metodo statico) può funzionare molto bene. È come una via di mezzo tra la tua idea e quella del tuo amico. L'unico inconveniente è che devi prestare molta attenzione quando aggiungi alcuni nuovi dati per non interrompere i casi di test esistenti (ma se aggiungi due o tre righe per tabella come abbiamo fatto, non dovrebbe essere un problema)


Non preferirei creare le parti del database necessarie ciascuna per il test piuttosto che qualcuno che modifichi accidentalmente i dati in modo da causare un errore? Dover assicurarsi che i dati siano corretti quando un test fallisce sembra qualcosa che può essere prevenuto.
Ryanzec,

1
La grande fase di installazione che inserisce i dati utili ai diversi casi di test può essere utile solo per i test di integrazione in cui è necessario controllare diverse parti dell'applicazione che lavorano insieme. Potrebbe valere la pena avere questo grande insieme comune di "inserti" perché molto probabilmente ne avrai bisogno per altri test di integrazione. Altrimenti, se stiamo parlando solo di unit test, sono assolutamente per avere un set di dati da inserire per ogni caso di test.
Jalayn,

1

Penso che devi restringere un esempio con il tuo collega e scoprire cosa significano esattamente. Potreste essere entrambi nella stessa pagina.

Esempio: verifica della tabella delle transazioni del conto

  1. Non vorresti provare a visualizzare questa tabella per un utente / account senza transazioni?
  2. Prova ad aggiungere il primo record e vedi se riesci a creare un saldo.
  3. Crea record quando sono già presenti record e controlla il saldo corrente e qualsiasi altra regola aziendale.
  4. Visualizza la tabella con i record esistenti e tutti gli altri CRUD.

Che tu lo ottenga eseguendo i passaggi 1 e 2 o iniziando con un database già in questo stato (ripristinare un backup?) Non sono sicuro che sia importante. La tua idea di crearmi uno script per me semplifica la gestione di tutte le modifiche di cui hai bisogno (come se avessi dimenticato di creare un account amministratore e ne avessi bisogno per un nuovo utente). I file di script sono più facili da mettere nel controllo del codice sorgente rispetto ad alcuni file di backup. Ciò dipende anche dalla distribuzione o meno di questa app.


0

Per disegnare insieme alcuni aspetti di alcune risposte e aggiungere il mio 2p ...

Nota: i miei commenti si riferiscono in particolare al test del database e non al test dell'interfaccia utente (anche se ovviamente simile si applica).

I database hanno bisogno di essere testati tanto quanto le applicazioni front-end, ma tendono a essere testati sulla base di "funziona con il front-end?" o 'i rapporti producono il risultato corretto?', che secondo me sta testando molto tardi nel processo di sviluppo del database e non molto robusto.

Abbiamo un numero di clienti che utilizzano test unitari / di integrazione / di sistema per il loro database di data warehouse oltre al consueto UAT / performance / et al. test. Scoprono che con una continua integrazione e test automatizzati rilevano molti problemi prima di arrivare all'UAT tradizionale, risparmiando così tempo in UAT e aumentando le possibilità di successo UAT.

Sono sicuro che la maggior parte concorderebbe sul fatto che un simile rigore dovrebbe essere applicato ai test di database come front-end o report test.

La cosa chiave con i test è testare piccole entità semplici, assicurandone la correttezza, prima di procedere su combinazioni complesse di entità, assicurandone la correttezza prima di espandersi nel sistema più ampio.

Quindi, dando un po 'di contesto alla mia risposta ...

Test unitari

  • ha un focus di prova per dimostrare che l'unità funziona, ad esempio una tabella, vista, funzione, procedura memorizzata
  • dovrebbe "stub" le interfacce per rimuovere le dipendenze esterne
  • fornirà i propri dati. È necessario uno stato iniziale noto dei dati, quindi se esiste la possibilità che i dati pre-test esistano, allora dovrebbero verificarsi troncamenti / eliminazioni prima della popolazione
  • verrà eseguito idealmente nel proprio contesto di esecuzione
  • chiarirà dopo se stesso e rimuoverà i dati utilizzati; questo è importante solo quando non vengono utilizzati stub.

I vantaggi di questo sono che si stanno rimuovendo tutte le dipendenze esterne dal test ed eseguendo il minor numero di test per dimostrare la correttezza. Ovviamente, questi test non possono essere eseguiti sul database di produzione. È possibile che ci siano diversi tipi di test che farai, a seconda del tipo di unità, tra cui:

  • controllo dello schema, alcuni potrebbero chiamare questo un test di "contratto dati"
  • valori della colonna che passano
  • l'esercizio di percorsi logici con diversi valori di dati per funzioni, procedure, viste, colonne calcolate
  • test dei casi limite - NULL, dati errati, numeri negativi, valori troppo grandi

(Unità) Test di integrazione

Ho trovato utile questo post SE nel parlare di vari tipi di test.

  • ha l'obiettivo di test per dimostrare che le unità si integrano insieme
  • eseguito su un numero di unità insieme
  • dovrebbe "stub" le interfacce per rimuovere le dipendenze esterne
  • fornirà i propri dati per rimuovere gli effetti di influenze esterne dei dati
  • verrà eseguito idealmente nel proprio contesto di esecuzione
  • chiarirà dopo se stesso e rimuoverà i dati creati; questo è importante solo quando non vengono utilizzati stub.

Passando dai test unitari a questi test di integrazione, spesso ci saranno leggermente più dati, al fine di testare una più ampia varietà di casi di test. Ovviamente, questi test non possono essere eseguiti sul database di produzione.

Questo procede quindi con System Testing , System Integration Testing (ovvero test end-2-end), con volumi di dati crescenti e portata crescente. Tutti questi test dovrebbero diventare parte di un framework di test di regressione. Alcuni di questi test potrebbero essere scelti dagli utenti per essere eseguiti come parte dell'UAT, ma UAT sono i test definiti dagli utenti , non come definiti dall'IT - un problema comune!

Quindi, ora che ho dato un po 'di contesto, per rispondere alle tue domande reali

  • la prepopolazione dei dati per i test di unità e integrazione può causare errori di test spuri e deve essere evitata.
  • L'unico modo per garantire test coerenti è di non fare ipotesi sui dati di origine e controllarli rigorosamente.
  • è importante un contesto di esecuzione del test separato, per garantire che un tester non sia in conflitto con un altro tester che esegue gli stessi test su un diverso ramo del codice del database controllato dalla sorgente.

-3

Francamente penso che se si eseguono test di unità senza un database all'incirca delle stesse dimensioni del database di produzione esistente, si avranno molte cose che superano i test e falliscono nella produzione per le prestazioni. Ovviamente sono contrario a persone che stanno sviluppando un piccolo database locale anche per questo motivo.

E se il codice è specifico per i dati, come puoi testarlo efficacemente senza dati? Ti mancherà vedere se le query hanno restituito i risultati corretti. Perché dovresti anche prendere in considerazione il test su un database vuoto, tutto ciò che ti dice è se la sintassi è corretta e non se la query è corretta. Mi sembra miope. Ho visto troppe cose che girano e superano test categoricamente sbagliati. Non vuoi trovarlo nei test unitari? Lo voglio.


Non sto suggerendo di correre contro un database vuoto, se vedi il passaggio 2 ho "Quindi inserisci tutti i dati necessari per i casi di test che stai per eseguire". Per quanto riguarda il problema delle prestazioni, non penso che sia a questo che serve il test unitario, ovvero più test di carico. Unità di test per me per testare per assicurarsi che la logica nel tuo codice funzioni. se la logica funziona, funzionerà per 1 record o 100.000.000.000 di record degli stessi dati di base (pensato che sarà molto più lento).
Ryanzec,

Le query del database non riguardano solo la logica e prima scoprirai che non funzionerà meglio. Ciò che funziona per 1 record spesso andrà in timeout su prod e lo shoudl di unit test lo dimostrerà il prima possibile.
HLGEM,

I test di unità e integrazione sono per funzionalità e non prestazioni, quindi è possibile eseguire test con piccole quantità di dati
user151019

I test unitari non dovrebbero mai usare un database - i test di integrazione usano database.
Nicholas Mayne,

1
Quello di cui stai effettivamente parlando è il test del carico. Se avessi una serie di test di accettazione e li avessi collegati a uno strumento di test del carico, sarai in grado di ottenere l'effetto desiderato.
Nicholas Mayne,
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.