2015-01-08 5 views
17

Ich bin relativ neu bei Node und arbeite an einem Projekt mit Knex und Bücherregal. Ich habe ein bisschen Ärger mit meinem Code und bin mir nicht sicher, was ich falsch mache.Komponententests mit Bookshelf.js und knex.js

Im Prinzip habe ich ein Modell (genannt VorcuProduct), die wie folgt aussieht:

var VorcuProduct = bs.Model.extend({ 
    tableName: 'vorcu_products' 
}); 

module.exports.VorcuProduct = VorcuProduct 

und eine Funktion, die eine VorcuProduct spart, wenn er nicht auf der DB nicht vorhanden ist. Ziemlich einfach. Die Funktion sieht so aus:

Welches ist der richtige Weg, dies zu testen, ohne die DB zu treffen? Muss ich fetch vortäuschen, um ein Modell oder undefined zurückzugeben (abhängig vom Test) und dann dasselbe mit save tun? Soll ich dafür rewire?

Wie Sie sehen können, bin ich ein wenig verloren, so dass jede Hilfe geschätzt wird.

Danke!

Antwort

16

Ich habe in-memory Sqlite3 databases für automatisierte Tests mit großem Erfolg verwendet. Meine Tests dauern 10 bis 15 Minuten, um mit MySQL zu laufen, aber nur 30 Sekunden mit einer sqlite3-Datenbank im Speicher. Verwenden Sie :memory: für Ihre Verbindungszeichenfolge, um diese Technik zu verwenden.

Ein Hinweis zur Unit Testing - Dies ist kein echter Komponententest, da wir immer noch eine Abfrage für eine Datenbank ausführen. Dies ist ein Test der technischen Integration, der jedoch innerhalb eines angemessenen Zeitraums abläuft. Wenn Sie eine anfragestarke Anwendung (wie meine) haben, wird sich diese Technik als effektiver erweisen, wenn Sie Fehler als nur Komponententests erfassen.

Ersetzt - Knex/Bookshelf initialisiert die Verbindung zu Beginn der Anwendung, dh Sie behalten den Kontext zwischen den Tests. Ich würde empfehlen, ein Schema create/destroy zu schreiben, damit Sie die Tabellen für jeden Test erstellen und löschen. Außerdem ist Sqlite3 gegenüber Fremdschlüsseleinschränkungen weniger sensibel als MySQL oder PostgreSQL. Stellen Sie daher sicher, dass Sie Ihre Anwendung immer wieder mit einer dieser Anwendungen ausführen, um sicherzustellen, dass Ihre Einschränkungen ordnungsgemäß funktionieren.

+0

Dank. Aus Neugier, wie viele Tests laufen Sie? Umfasst das Setup auch das Laden einer signifikanten Menge an Seed-Daten? – thebearingedge

+1

@thebearingegedge Ich laufe ungefähr 70 Szenarien mit insgesamt etwa 1.000 Gurkenschritten. Ich richte und zerlege 60 Tabellen für jedes Szenario. Mit sqlite In-Memory dauert es dafür weniger als eine halbe Sekunde. –

3

Dies ist eigentlich eine gute Frage, die sowohl den Wert als auch die Grenzen des Komponententests aufzeigt.

In diesem speziellen Fall ist die non-stubbed Logik ziemlich einfach - nur ein einfacher if Block, also ist es fraglich, ob es das Unit Test-Aufwand wert ist, also ist die akzeptierte Antwort eine gute und zeigt den Wert an von kleinen Integrationstests.

Auf der anderen Seite ist die Ausübung von Komponententests immer noch wertvoll, da es Möglichkeiten für Codeverbesserungen aufzeigt. Im Allgemeinen, wenn die Tests zu kompliziert sind, kann der zugrundeliegende Code wahrscheinlich etwas Refactoring verwenden. In diesem Fall kann eine doesProductExist Funktion wahrscheinlich umgestaltet werden. Die Rückgabe der Versprechen von knex/bookshelf, anstatt in Rückrufe zu konvertieren, wäre ebenfalls eine hilfreiche Vereinfachung.

Aber zum Vergleich hier ist mein nehmen auf, was wahre Einheit-Prüfung des vorhandenen Code aussehen würde:

Ihre Erfahrungen für den Austausch von
var rewire = require('rewire'); 
var sinon = require('sinon'); 
var expect = require('chai').expect; 
var Promise = require('bluebird'); 
var subscribeToUpdatesModule = rewire('./service/subscribe_to_updates_module'); 

var subscribeToUpdates = subscribeToUpdatesModule.__get__(subscribeToUpdates); 

describe('subscribeToUpdates', function() { 
    before(function() { 
    var self = this; 
    this.sandbox = sinon.sandbox.create(); 
    var VorcuProduct = subscribeToUpdatesModule.__get__('model').VorcuProduct; 

    this.saveStub = this.sandbox.stub(VorcuProduct.prototype, 'save'); 
    this.saveStub.returns(this.saveResultPromise); 

    this.fetchStub = this.sandbox.stub() 
    this.fetchStub.returns(this.fetchResultPromise); 

    this.sandbox.stub(VorcuProduct, 'where', function() { 
     return { fetch: self.fetchStub }; 
    }) 

    }); 

    afterEach(function() { 
    this.sandbox.restore(); 
    }); 

    it('calls save when fetch of existing_model succeeds', function (done) { 
    var self = this; 
    this.fetchResultPromise = Promise.resolve('valid result'); 
    this.saveResultPromise = Promise.resolve('save result'); 
    var callback = function (err, result) { 
     expect(err).to.be.null; 
     expect(self.saveStub).to.be.called; 
     expect(result).to.equal('save result'); 
     done(); 
    }; 
    subscribeToUpdates({}, callback); 
    }); 

    // ... more it(...) blocks 

});