Datenbank in node.js verspotten?

79

Wie würde ich die Datenbank in meiner Anwendung node.js verspotten, die in diesem Fall mongodbals Backend für eine Blog-REST-API verwendet wird?

Sicher, ich könnte die Datenbank auf eine bestimmte Datenbank testingeinstellen, aber ich würde trotzdem Daten speichern und nicht nur meinen Code, sondern auch die Datenbank testen, also mache ich eigentlich keine Unit-Tests, sondern Integrationstests.
Was soll man also tun? Erstellen Sie Datenbank-Wrapper als mittlere Ebene zwischen Anwendung und Datenbank und ersetzen Sie die DAL beim Testen?

// 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();
    });

  });
));
Industriell
quelle

Antworten:

128

Ich glaube nicht, dass datenbankbezogener Code ordnungsgemäß getestet werden kann, ohne ihn mit der Datenbanksoftware zu testen. Das liegt daran, dass der Code, den Sie testen, nicht nur Javascript ist, sondern auch die Datenbankabfragezeichenfolge. Auch wenn in Ihrem Fall die Abfragen einfach aussehen, können Sie sich nicht darauf verlassen, dass dies für immer so ist.

Daher implementiert jede Datenbankemulationsschicht notwendigerweise die gesamte Datenbank (möglicherweise ohne Festplattenspeicher). Bis dahin führen Sie Integrationstests mit dem Datenbankemulator durch, obwohl Sie dies als Komponententest bezeichnen. Ein weiterer Nachteil ist, dass der Datenbankemulator möglicherweise andere Fehler aufweist als die Datenbank, und dass Sie möglicherweise sowohl für den Datenbankemulator als auch für die Datenbank codieren müssen (ähnlich wie bei IE vs Firefox vs Chrome usw.). ).

Daher besteht meiner Meinung nach die einzige Möglichkeit, Ihren Code korrekt zu testen, darin, ihn mit der realen Datenbank zu verbinden.

Slebetman
quelle
1
Sie machen einen guten Punkt. Während Unit-Tests einem phänomenalen Zweck dienen (dh Isolation), haben Sie beim Integrationstest einen starken Punkt gemacht.
Mike Perrenoud
4
@MichaelPerrenoud: Ich mag die Regel, die in der Antwort von christkv festgelegt ist: " Verspotte nichts, was du nicht besitzt" . Es geht zwar nicht ins Detail, warum es eine schlechte Idee ist, aber es ist eine leicht zu merkende Regel.
Slebetman
1
Ich bin mit dieser Antwort nicht einverstanden, in Meteorjs haben sie beim Ausführen von Tests irgendwie eine Test-DB eingerichtet (ich nehme an, es ist keine Scheinbibliothek, sondern eine temporäre Datei) und es ist sehr praktisch. Es wäre sehr nützlich, ein Objekt zu haben, das sich genau wie ein Mongodb verhält und nach sich selbst reinigt. Ob sich alles im Speicher oder in einer temporären Datei befindet, ist ein Implementierungsdetail, sodass Sie keinen Code duplizieren müssen. Ich bin damit einverstanden, dass die Leute, die den Fahrer herstellen, diejenigen sein sollten, die das Scheinobjekt herstellen.
Uri
Ich glaube, ich bin auch mit dieser Antwort nicht einverstanden. Die Frage von OP war insofern einfach, als es sich nur um einen Controller und die Datenbank handelt. Was ist mit der gesamten Anwendungslogik, die Server zu Servern macht und nicht nur Pipes von der Datenbank zum Internet? Stellen Sie sich vor, Sie haben ein Objekt, das Sie validieren und dann in die Datenbank aufnehmen möchten. Stellen Sie sich nicht so etwas wie einen Integrationstest vor, um zu überprüfen, ob die Interaktion noch funktioniert, und Unit-Tests, um die gesamte Validierung zu überprüfen. Wenn man diese Antwort wörtlich nimmt, wie kann man etwas mit Daten testen, die aus der Datenbank stammen? Wie lange dauern deine Builds?
user2152081
1
Schauen Sie, das Verspotten von DB-Aufrufen ist zutiefst albern und führt nur zu viel Arbeit. Ich denke, die Leute verpassen den ganzen Sinn der Tests. Es soll zeigen, dass die Eingabe mit der Ausgabe übereinstimmt. Bei einer Funktion, die eine Datenbank aufruft, können Sie dies nur dann sicher wissen, wenn dieser Test den Datenbankaufruf umfasst. Der beste Weg, dies zu tun, besteht darin, dass Ihre Testkonfiguration auf eine Instanz von MongoDB verweist, die im Speicher ausgeführt wird. Auf diese Weise müssen Sie sich nicht mit Festplatten-E / A befassen und erfüllen auch den Hauptpunkt des Verspottens, nämlich das Ausführen aus dem Speicher.
Glstunna
42

Es gibt eine allgemeine Faustregel, wenn es um das Verspotten geht

Verspotten Sie nichts, was Sie nicht besitzen.

Wenn Sie die Datenbank verspotten möchten, verstecken Sie sie in einer abstrahierten Service-Schicht und verspotten Sie diese Schicht. Stellen Sie dann sicher, dass Sie die Integration der tatsächlichen Service-Schicht testen.

Persönlich habe ich mich von der Verwendung von Mocks zum Testen verabschiedet und sie für das Design von oben nach unten verwendet, um die Entwicklung von oben nach unten voranzutreiben und dabei die Service-Layer zu verspotten und schließlich diese Layer zu implementieren und Integrationstests zu schreiben. Als Testwerkzeug verwendet, neigen sie dazu, Ihren Test sehr spröde zu machen und führen im schlimmsten Fall zu einer Abweichung zwischen tatsächlichem Verhalten und verspottetem Verhalten.

christkv
quelle
Sicher können Sie es hinter einem Repository oder Gateway verstecken und den Mock verwenden, um Ihren testgetriebenen Ansatz voranzutreiben und Ihre Komponententests zu isolieren. Was meinen Sie damit, dass Sie dann keine Mocks zum Testen verwenden? Sie werden dieses verspottete Gateway / Repository noch in Ihren Tests behalten und dann irgendwie eine Schnittstelle verwenden, um die tatsächliche Implementierung in Ihrem Repository über eine Schnittstelle anzugeben, oder?
PositiveGuy
Wie gehen Sie mit APIs von Drittanbietern um? Ein VCR-ähnlicher Helfer?
Dogweather
Sie sind der beste Herr!
Marco Lazzeri
40

Ich bin mit der ausgewählten Antwort oder anderen Antworten bisher nicht einverstanden.

Wäre es nicht großartig, wenn Sie Fehler abfangen könnten, die durch die chaotischen und oft chaotischen Änderungen an DB-Schemas und Ihrem Code hervorgerufen wurden, BEVOR es zur Qualitätssicherung kommt? Ich wette, die Mehrheit würde zum Teufel ja schreien!

Sie können und sollten Ihre DB-Schemas mit Sicherheit isolieren und testen. Und Sie tun dies nicht basierend auf einem Emulator oder einem umfangreichen Image oder einer Neuerstellung Ihrer Datenbank und Ihres Computers. Dies ist nur ein Beispiel für SQLite. Sie verspotten es basierend auf einer im Speicher ausgeführten Instanz, die ausgeführt wird, und mit statischen Daten, die sich in der in der Speicherinstanz nicht ändern. Dies bedeutet, dass Sie Ihre Datenbank wirklich isoliert testen und Ihren Tests auch vertrauen können. Und offensichtlich ist es schnell, weil es sich im Speicher befindet, ein Skelett, und am Ende eines Testlaufs verschrottet wird.

Ja, Sie sollten und Sie sollten das SCHEMA testen, das in eine sehr kompakte Speicherinstanz der von Ihnen verwendeten DB-Engine / Laufzeit exportiert wird, und das zusammen mit dem Hinzufügen einer sehr kleinen Menge statischer Daten zu Ihrer isolierten verspotteten DB wird.

Sie exportieren Ihre realen Schemas regelmäßig (automatisiert) aus Ihrer realen Datenbank und importieren / aktualisieren diese in Ihre Light-in-Memory-DB-Instanz vor jedem Push an die Qualitätssicherung. Sie werden sofort wissen, ob die neuesten DB-Änderungen von Ihren DB-Administratoren oder anderen vorgenommen wurden Entwickler, die das Schema in letzter Zeit geändert haben, haben alle Tests abgebrochen.

Obwohl ich die Bemühungen begrüße, Ihr Bestes zu geben, um zu antworten, würde ich die aktuelle Antwort ablehnen, wenn ich könnte, aber ich bin neu und habe noch nicht genug Ruf aufgebaut, um meine Fähigkeit dazu zu ermöglichen.

Was die Person betrifft, die mit "Verspotten Sie nichts, was Sie nicht besitzen" geantwortet hat. Ich denke, er wollte sagen "Teste nichts, was du nicht besitzt". Aber du verspottest Dinge, die du nicht besitzt! Denn das sind die Dinge, die nicht getestet werden und die isoliert werden müssen!

Ich habe vor, das WIE mit Ihnen zu teilen und werde diesen Beitrag zu einem späteren Zeitpunkt mit echtem Beispiel-JS-Code aktualisieren!

Dies ist, was viele testgetriebene Teams ständig tun. Man muss nur das Wie verstehen.

PositiveGuy
quelle
5
Ich würde gerne upvoten, aber ich kann nicht, wenn es die Frage nicht mildert und eine Lösung bietet. Bitte aktualisieren Sie Ihren Beitrag, wenn Sie eine Chance bekommen.
Shanimal
5
2018 und trotzdem gibt es kein "wie", war sicherlich daran interessiert mehr zu lesen.
MacK
3
Ich habe seit 3 ​​Jahren abgestimmt, es gibt immer noch kein Wie.
ian
5

Mein bevorzugter Ansatz für Unit-Test-DB-Code in einer beliebigen Sprache ist der Zugriff auf Mongo über eine Repository-Abstraktion (ein Beispiel finden Sie hier http://iainjmitchell.com/blog/?p=884 ). Die Implementierungen variieren in Bezug auf die DB-spezifischen Funktionen. Wenn Sie jedoch den gesamten Mongo-Code aus Ihrer eigenen Logik entfernen, können Sie Unit Test durchführen. Ersetzen Sie einfach die Mongo Repository-Implementierung durch eine abgespeckte Version, die trivial einfach ist. Speichern Sie beispielsweise Objekte einfach in einer einfachen In-Memory-Wörterbuchsammlung.

Auf diese Weise erhalten Sie die Vorteile eines Unit-Tests Ihres eigenen Codes ohne DB-Abhängigkeiten. Sie müssen jedoch weiterhin Integrationstests für die Haupt-DB durchführen, da Sie die Eigenheiten der realen Datenbank wahrscheinlich nie so emulieren können wie andere sagte hier. Die Art von Dingen, die ich gefunden habe, ist so einfach wie das Indizieren im abgesicherten Modus oder ohne abgesicherten Modus. Insbesondere wenn Sie einen eindeutigen Index haben, kann Ihre Dummy-Speicherimplementierung dies in allen Fällen berücksichtigen, Mongo jedoch nicht ohne abgesicherten Modus.

Während Sie für einige Vorgänge noch Tests mit der Datenbank durchführen müssen, können Sie Ihre eigene Logik mit einer ausgeblendeten Repository-Implementierung auf jeden Fall ordnungsgemäß testen.

Cirrus
quelle
Aber irgendwann muss Ihr realer Implementierungscode auf die realen Datenaufrufe verweisen. Ich gehe davon aus, dass Sie die Schnittstelle in Ihr Repository in den realen Datenschicht-Abfragecode einfügen.
PositiveGuy
1
Versuchen Sie es mit Docker. Ich habe jahrelange albtraumhafte Konfigurationsprobleme mit Datenbanken und Paketinstallationen gelöst, indem ich Docker verwendet habe, um Container auszuführen, auf denen Datenbanken ausgeführt werden, die mit bestimmten Testdaten für das Szenario initialisiert wurden. Tatsächlich führe ich Stapel von 3 Containern aus: einen mit der Datenbank, einen mit dem Anwendungscode und einen mit dem Testtreiber. Wenn Ihr Datensatz mäßig groß ist, können Sie sogar parallele Instanzen dieser Stapel hochfahren, wodurch sich Ihr Testzyklus erheblich verkürzt.
Ovo
4

Der Zweck des Verspottens besteht darin, den eigenen Code für Komplexität und Komponententest zu überspringen. Wenn Sie e2e-Tests schreiben möchten, verwenden Sie die Datenbank.

Das Schreiben von Code zum Einrichten / Herunterfahren einer Test-DB für Unit-Tests ist eine technische Schuld und unglaublich unbefriedigend.

Es gibt Scheinbibliotheken in npm:

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

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

Wenn diese die von Ihnen benötigten Funktionen nicht unterstützen, müssen Sie möglicherweise die reale Funktion verwenden.

Michael Cole
quelle
1
mongo-mock unterstützt zum Beispiel keine Aggregationsoperationen, was ein Problem sein kann, das ich angefangen habe, und am Ende habe ich die echte Software mit einer testOnly-Datenbank verwendet
zardilior
Auch beim Wechsel von Mongo-Mock zu Mongodb sind viele Dinge kaputt gegangen, so dass es den Zweck
zunichte gemacht hat
3

Ich hatte dieses Dilemma und entschied mich, mit einer Test-DB zu arbeiten und sie jedes Mal zu reinigen, wenn der Test beginnt. (So ​​lassen Sie alles fallen: https://stackoverflow.com/a/25639377/378594 )

Mit NPM können Sie sogar ein Testskript erstellen, das die Datenbankdatei erstellt und anschließend bereinigt.

Uri
quelle
Dies ist eigentlich ein guter Ansatz, und ich würde jedem empfehlen, der ernsthaftes CI / CD-Pipelining durchführt, dies zu tun. Mit den heutigen JavaScript-Tools können wir Testdatenbanken einfach vor und nach dem Ausführen von Tests erstellen / löschen. Ich würde argumentieren, ob dieser Ansatz während der Entwicklung geeignet ist (Sie möchten Ihre Datenbank nicht bei jeder Codeänderung löschen und erstellen), aber dafür gibt es eine andere Lösung. Zumindest wird das Problem gelöst, dass separate Plugin-Integrationen, die Daten verspotten, beibehalten und an Ihre Testumgebung geklebt werden müssen.
Nicky