2012-09-21 17 views
59

Come faccio a prendere in giro il database nella mia applicazione node.js, che in questo caso utilizza mongodb come backend per un'API REST del blog?Database di simulazione in node.js?

Certo, potrei impostare il database su uno specifico testing -database, ma vorrei comunque salvare i dati e non testare solo il mio codice, ma anche il database, quindi in realtà non sto facendo test unitari ma test di integrazione.
Quindi cosa si dovrebbe fare? Creare wrapper di database come livello intermedio tra applicazione e db e sostituire il DAL quando si esegue il test?

// app.js 
var express = require('express'); 
    app = express(), 
    mongo = require('mongoskin'), 
    db = mongo.db('localhost:27017/test?auto_reconnect'); 

app.get('/posts/:slug', function(req, res){ 
    db.collection('posts').findOne({slug: req.params.slug}, function (err, post) { 
     res.send(JSON.stringify(post), 200); 
    }); 
}); 

app.listen(3000); 

// test.js 
r = require('requestah')(3000); 
describe("Does some testing", function() { 

    it("Fetches a blogpost by slug", function(done) { 
    r.get("/posts/aslug", function(res) { 
     expect(res.statusCode).to.equal(200); 
     expect(JSON.parse(res.body)["title"]).to.not.equal(null); 
     return done(); 
    }); 

    }); 
)); 

risposta

85

non credo codice relativo database può essere adeguatamente testato senza prove con il software di database. Questo perché il codice che stai testando non è solo javascript, ma anche la stringa di query del database. Anche se nel tuo caso le domande sembrano semplici, non puoi fare affidamento su di esso in questo modo per sempre.

Pertanto, qualsiasi livello di emulazione del database implementerà necessariamente l'intero database (forse meno l'archiviazione su disco). A quel punto finisci col fare test di integrazione con l'emulatore del database anche se lo chiami test delle unità. Un altro svantaggio è che l'emulatore del database può finire per avere un diverso set di bug rispetto al database e si può finire per dover codificare sia per l'emulatore di database e il database (un po 'come la situazione con IE vs Firefox vs Chrome ecc.).

Pertanto, a mio parere, l'unico modo per verificare correttamente il codice è l'interfaccia con il database reale.

+1

Sai, sei un buon punto. Mentre i test unitari hanno uno scopo fenomenale (ad esempio l'isolamento), hai reso un punto di forza per i test di integrazione. –

+3

@MichaelPerrenoud: Mi piace la regola stabilita dalla risposta di christkv: ** "Non prendere in giro tutto ciò che non possiedi" **. Anche se non entra nei dettagli perché è una cattiva idea, è una regola facile da ricordare. – slebetman

+1

Non sono d'accordo con questa risposta, in meteorjs hanno impostato un DB di test in qualche modo durante l'esecuzione di test (presumo che non sia una libreria di simulazione, ma un file temporaneo) ed è molto conveniente. Sarebbe molto utile avere un oggetto che si comporta esattamente come un mongodb e si pulisce da solo. Se è tutto in memoria o un file temporaneo è il dettaglio dell'implementazione, quindi non devi duplicare il codice. Sono d'accordo che le persone che fanno il driver dovrebbero essere quelle che fanno l'oggetto finto. – Uri

36

C'è una regola generale da seguire quando si tratta di scherno che è

Non deridere tutto ciò che non si possiede.

Se si vuole prendere in giro il db nascondersi dietro un livello di servizio astratto e deridere quel livello. Quindi assicurati che l'integrazione test il livello di servizio effettivo.

Personalmente mi sono allontanato dall'usare i simulatori per il test e li ho utilizzati per la progettazione dal basso verso l'alto, aiutandomi a guidare lo sviluppo dall'alto verso il basso prendendo in giro i livelli di servizio mentre andavo e poi implementando questi livelli e scrivendo i test di integrazione . Utilizzati come strumento di test tendono a rendere il test molto fragile e nel peggiore dei casi porta a una divergenza tra comportamento effettivo e comportamento deriso.

+0

Certo, puoi nasconderlo dietro un repository o un gateway e usare la simulazione per guidare il tuo approccio basato su test e per isolare i tuoi test unitari ... cosa vuoi dire che non utilizzi i mock per i test? Continuerai a tenere quel maledetto gateway/repository ancora nei tuoi test, quindi in qualche modo usare un'interfaccia per specificare l'effettiva implementazione nel tuo repository attraverso un'interfaccia, giusto? – PositiveGuy

3

Il mio approccio preferito al codice del DB di test unitario in qualsiasi lingua è accedere a Mongo tramite un'astrazione di repository (c'è un esempio qui http://iainjmitchell.com/blog/?p=884). Le implementazioni varieranno in termini di funzionalità specifiche di DB esposte, ma rimuovendo tutto il codice Mongo dalla tua logica sei in grado di eseguire il Test unità. Semplicemente sostituisci l'implementazione di Mongo Repository con una versione stoppata che è banalmente semplice. Ad esempio, è sufficiente memorizzare gli oggetti in una semplice raccolta di dizionari in memoria.

Otterrete i vantaggi di unità di testare il proprio codice in questo modo, senza dipendenze DB ma avrete ancora bisogno di fare test di integrazione nei confronti del DB principale perché probabilmente mai in grado di emulare le idiosincrasie del reale database come altri hanno detto qui. Il tipo di cose che ho trovato sono semplici come l'indicizzazione in modalità sicura vs senza modalità sicura. In particolare, se si dispone di un indice univoco, l'implementazione della memoria fittizia potrebbe onorarla in tutti i casi, ma Mongo non lo farà senza la modalità provvisoria.

Quindi, anche se è necessario testare il DB per alcune operazioni, sarà certamente possibile testare correttamente la propria logica con un'implementazione del repository stoppata.

+0

ma a un certo punto il tuo vero codice di implementazione deve fare riferimento alle chiamate dati reali. Presumo che tu stia iniettando l'interfaccia nel tuo repository al codice di query del livello dati reale? – PositiveGuy

+0

Provare ad usare Docker. Ho risolto anni di problemi di configurazione da incubo con database e installazioni di pacchetti utilizzando Docker per eseguire contenitori che eseguono database inizializzati con dati di test specifici per lo scenario. In effetti eseguo pile di 3 contenitori: uno con il DB, uno con il codice dell'applicazione e uno con il driver di test. Se il tuo set di dati è di dimensioni moderate, puoi persino creare istanze parallele di questi stack, accorciando notevolmente il tuo ciclo di test. – Raidex

32

Non sono d'accordo con la risposta selezionata o altre risposte finora.

Non sarebbe fantastico se riuscissi a catturare gli errori generati dalle modifiche caotiche e molte volte disordinate apportate agli schemi DB e al tuo codice PRIMA che arrivi al QA? Scommetto che la maggioranza griderebbe sì!

È sicuramente possibile e deve isolare e testare gli schemi DB. E tu non lo fai basandosi su un emulatore o su un'immagine pesante o sulla ricreazione del tuo DB e della tua macchina. Questo è ciò che roba come SQLite è solo per un esempio. Si prende in giro basandosi su un'istanza di memoria leggera in esecuzione e con dati statici che non cambiano in quanto in istanza di memoria, il che significa che si sta veramente testando il DB in isolamento e si può fidare anche dei test. E ovviamente è veloce perché è in memoria, uno scheletro, ed è scartato alla fine di una corsa di prova.

Quindi sì dovresti e dovresti testare lo SCHEMA che viene esportato in un'istanza di memoria molto leggera di qualunque motore/runtime DB che stai usando, e che insieme all'aggiunta di una quantità molto piccola di dati statici diventa il tuo deriso isolato DB.

Esportate periodicamente i vostri schemi reali dal vostro DB reale (in modo automatico) e importateli/aggiornateli nella memoria in istanza di DB di memoria prima di ogni passaggio al QA e saprete immediatamente se eventuali modifiche di DB più recenti eseguite dal vostro Gli amministratori di DB o altri sviluppatori che hanno modificato lo schema ultimamente hanno interrotto qualsiasi test.

Mentre applaudo lo sforzo di fare del proprio meglio per rispondere, vorrei abbassare il voto della risposta attuale se potessi, ma sono nuovo e non ho ancora costruito una reputazione sufficiente per consentire la mia capacità di farlo ancora.

Per quanto riguarda la persona che ha risposto con "non prendere in giro tutto ciò che non si possiede". Penso che intendesse dire "non testare nulla che non possiedi". Ma fai finta di cose che non possiedi! Perché quelle sono le cose non sotto test che devono essere isolate!

Ho intenzione di condividere il HOW con voi e aggiornerò questo post in un secondo momento con il codice JS di esempio reale!

Questo è ciò che molti team di test driven fanno sempre. Devi solo capire come.

+8

eventuali aggiornamenti sul come ancora ?? – ChickenWing24

+3

Mi piacerebbe molto più avanti, ma non posso se non attenua la domanda e fornire una soluzione. Si prega di aggiornare il tuo post quando ne hai la possibilità. – Shanimal

2

Ho avuto questo dilemma e ho scelto di lavorare con un DB di test e pulirlo ogni volta che inizia il test. (come rilasciare tutto: https://stackoverflow.com/a/25639377/378594)

Con NPM è anche possibile creare uno script di test che crea il file db e lo ripulisce dopo.

+0

questo è in realtà un buon approccio e consiglio a chiunque stia facendo un serio pipelining CI/CD per farlo. Con gli strumenti JavaScript di oggi siamo in grado di creare/rilasciare database di test facilmente prima e dopo aver eseguito i test. Vorrei discutere se questo approccio è adatto durante lo sviluppo (non si vuole perdere e creare il database su ogni cambiamento di codice), ma c'è un'altra soluzione per questo.Almeno risolve il problema di dover mantenere integrazioni di plug-in separate che simulano i dati e li incollano all'ambiente di test. – Nicky

4

Cosa ?! Perché deridere qualcosa allora? Lo scopo del mocking è saltare la complessità e il codice del test dell'unità. Se vuoi scrivere test e2e usa il db.

Scrittura del codice per l'installazione/smontaggio di un DB di test per il test dell'unità è un debito tecnico e incredibilmente insoddisfacente.

Ci sono biblioteche finte in NPM:

mongo - https://www.npmjs.com/package/mongomock

mangusta - https://www.npmjs.com/package/mockgoose

Se quelli non supportano le funzioni necessarie, allora sì potrebbe essere necessario utilizzare la cosa reale .

+1

lol "mockgoose" top 5 più divertente nome pacchetto NPM imo –

+0

IMHO è meglio fingere che simulare - https://jsmockito.org/ –