Test unitari - App accoppiata al database


15

Quale sarebbe l'approccio migliore per testare un'unità di un modello che si integra in un'applicazione strettamente collegata al database?

Lo scenario specifico qui è un carrello della spesa: mi piacerebbe poter testare l'aggiunta di rimozione e recupero di articoli dal carrello, nonché la logica dei prezzi, ecc. Tutto ciò nella mia mente richiede l'accesso al database anche se ho letto più volte che l'accesso al database dovrebbe essere evitato.


1
Interessante che le risposte che effettivamente dicono "riscrivi il tuo codice app" vengano votate
AD7six

Risposte:


10

L'iniezione di dipendenza è un modo per gestirlo. È possibile impostare un database di test per imitare il carrello oppure scrivere anche un codice che "conferma" la transazione del cliente. Quindi in fase di esecuzione, il software sceglierà a quale componente connettersi.

Basta non connettersi al database di produzione per nulla durante i test!


1
Con DI e una corretta progettazione dell'applicazione, dovresti essere in grado di testare senza alcun database --- a condizione che il finto che inietti fornisca un derisione abbastanza dettagliata del database back-end.
Peter K.

4

Nel test unitario, devi definire il limite di ciò che stai testando. Il test unitario è diverso dal test di integrazione. Se la logica dei prezzi è indipendente dal contenuto del carrello, lo si verifica separatamente. In caso contrario, e tutti i moduli sono strettamente accoppiati, crea un ambiente di test che imiti la produzione il più possibile e lavora con quello. Non credo che scorciatoie e simulazioni aiutino nel lungo periodo.


2

Il modello non dovrebbe dipendere da un DB (concreto). Se conosce solo un DB astratto (leggi "interfaccia") che viene passato al modello, puoi sostituire il DB con un oggetto simulato .

Nella programmazione orientata agli oggetti , gli oggetti simulati sono oggetti simulati che imitano il comportamento di oggetti reali in modo controllato. Un programmatore in genere crea un oggetto finto per testare il comportamento di qualche altro oggetto, più o meno allo stesso modo in cui un progettista di auto utilizza un manichino da crash test per simulare il comportamento dinamico di un essere umano negli impatti del veicolo ...


1

Ho avuto un problema simile: non avevo la possibilità di garantire che il mio DB di prova mantenga i valori. Quindi in futuro avrò ad esempio altri prezzi.

Ho estratto i dati di cui avevo bisogno in un piccolo sqlite -DB e ho usato questo DB per i miei test. Il Test-DB ora fa parte della configurazione del mio test unitario.


2
Il punto dei test unitari è testare il codice in modo isolato. Se si utilizza un db sqllite, non è isolato. Anche incoerenze tra i database possono causare errori
Tom Squires

0

"Best" è soggettivo, ma potresti semplicemente usare una connessione db di prova.

Utilizzare i dispositivi per caricare alcuni dati di test (ad esempio prodotti da acquistare) e quindi scrivere il case test per la classe / funzione che si desidera testare.


Descrivere unit test che testano una funzione che agisce su un database come test di integrazione è abbastanza fuorviante @murph.
AD7,

1
Ok, ora sono profondamente confuso - se coinvolge un database non è per la maggior parte un test unitario perché non è autonomo. Se si dispone di un database, si stanno eseguendo test a un livello superiore, uno con dipendenze, uno che guarda alla "combinazione" di cose. Indipendentemente da ciò, questa non è una chiara spiegazione per la mia mente su come risolvere il problema.
Murph

0

Ho creato un plugin per Symfony 1.4 (PHP) per affrontare questo problema (tra gli altri). È modellato sul modo in cui funziona il framework di test di Django (Python) : il framework crea e popola un database di test separato prima dell'inizio di ogni test e distrugge il database di test dopo il completamento di ogni test.

Avevo un paio di preoccupazioni su questa strategia, sia in termini di prestazioni (se lo schema non cambia, perché non semplicemente cancellare i dati invece di ricostruire l'intera struttura?) Sia di convenienza (a volte voglio ispezionare il database dopo un test fallito, quindi non distruggerlo indiscriminatamente!), quindi ho adottato un approccio leggermente diverso.

Prima dell'esecuzione del primo test, il database viene distrutto e ricostruito, nel caso in cui siano stati apportati cambiamenti al modello dall'ultimo test. Prima di eseguire ogni test successivo, i dati nel database vengono cancellati, ma la struttura non viene ricostruita (sebbene una ricostruzione manuale possa essere attivata da un test se necessario).

Caricando selettivamente i dispositivi di dati in ciascun test, è possibile creare l'ambiente adeguato per quel test senza interferire con i test successivi. I file di Fixture possono anche essere riutilizzati, il che rende questo compito molto meno oneroso (anche se è ancora la mia parte meno preferita dei test di scrittura!).

In entrambi i framework di test, l'adattatore del database è configurato per utilizzare la connessione di test anziché la connessione "produzione" per impedire che l'esecuzione di test danneggi i dati esistenti.


0

Direi, basta andare avanti e usare dispositivi per precaricare i dati. È il modo in cui i framework di unit test sembrano funzionare in generale, quando si testano la manipolazione dei dati.

Ma se vuoi davvero evitare di dover connetterti a un database di qualsiasi tipo e seguire la definizione troppo rigorosa che i test unitari non toccano nulla al di fuori del codice, dai un'occhiata al derisione degli oggetti: potrebbe darti idee.

Ad esempio, invece di rilasciare l'SQL direttamente nel codice dove serve, avere un modo per chiamare un metodo che fa solo quello che fa SQL. Utilizzare Person.getPhoneNumber(), ad esempio, anziché SELECT phone_number FROM person WHERE id = <foo>. Non solo è più semplice e intuitivo da capire a colpo d'occhio, ma durante i test è possibile prendere in giro l'oggetto Person in modo che getPhoneNumber()ritorni sempre 555-555-5555o qualcosa del genere, invece di toccare il database.


0

Questo è abbastanza facile da fare con junit se un po 'lungo e tortuoso.

Il "setup" dovrebbe definire e popolare un set di tabelle temporanee.

È quindi possibile eseguire i test unitari per tutte le funzionalità di aggiornamento, inserimento, eliminazione.

Per ogni test si chiama il metodo di aggiornamento, quindi si esegue un po 'di SQL per verificare il risultato previsto.

Nella fase di "abbattimento" si eliminano tutte le tabelle.

In questo modo si eseguono sempre gli stessi test sugli stessi dati iniziali. Se si mantengono le tabelle tra i test finiscono per essere "inquinate" da test falliti, inoltre, un test di "inserimento" coerente è quasi impossibile in quanto è necessario continuare a inventare nuove chiavi su ogni test.

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.