Auf dieser Seite werden Best Practices und Tools zum Schreiben von Unit-Tests für Ihre
Funktionen beschrieben, z. B. Tests, die Teil eines CI-Systems (Continuous Integration)
sein könnten. Um das Testen zu erleichtern, bietet Firebase das Firebase Test SDK für Cloud Functions. Es
wird auf npm als firebase-functions-test verteilt und ist ein begleitendes Test-SDK
zu firebase-functions. Das Firebase Test SDK für Cloud Functions:
- Es kümmert sich um die entsprechende Einrichtung und Bereinigung für Ihre Tests, z. B. das
Festlegen und Aufheben von Umgebungsvariablen, die von
firebase-functionsbenötigt werden. - Es generiert Beispieldaten und Ereigniskontext, sodass Sie nur die Felder angeben müssen, die für Ihren Test relevant sind.
Testeinrichtung
Installieren Sie sowohl firebase-functions-test als auch Mocha, ein
Test-Framework, indem Sie die folgenden Befehle in Ihrem Funktionenordner ausführen:
npm install --save-dev firebase-functions-test
npm install --save-dev mocha
Erstellen Sie als Nächstes einen test-Ordner im Funktionenordner, erstellen Sie darin eine neue Datei
für Ihren Testcode und geben Sie ihr einen Namen wie index.test.js.
Ändern Sie schließlich functions/package.json, um Folgendes hinzuzufügen:
"scripts": {
"test": "mocha --reporter spec"
}
Nachdem Sie die Tests geschrieben haben, können Sie sie ausführen, indem Sie npm test in
Ihrem Funktionenverzeichnis ausführen.
Firebase Test SDK für Cloud Functions initialisieren
Es gibt zwei Möglichkeiten, firebase-functions-test zu verwenden:
- Onlinemodus (empfohlen) : Schreiben Sie Tests, die mit einem Firebase-Projekt interagieren, das für Tests vorgesehen ist, sodass Datenbankschreibvorgänge, Nutzererstellungen usw. tatsächlich ausgeführt werden und Ihr Testcode die Ergebnisse prüfen kann. Das bedeutet auch, dass andere Google SDKs, die in Ihren Funktionen verwendet werden, ebenfalls funktionieren.
- Offlinemodus: Schreiben Sie isolierte und Offline-Unit-Tests ohne Nebeneffekte. Das bedeutet, dass alle Methodenaufrufe, die mit einem Firebase-Produkt interagieren (z.B. in die Datenbank schreiben oder einen Nutzer erstellen), als Stubs implementiert werden müssen. Die Verwendung des Offlinemodus wird im Allgemeinen nicht empfohlen, wenn Sie Cloud Firestore oder Realtime DatabaseFunktionen haben, da dies die Komplexität Ihres Testcodes erheblich erhöht.
SDK im Onlinemodus initialisieren (empfohlen)
Wenn Sie Tests schreiben möchten, die mit einem Testprojekt interagieren, müssen Sie die
Projektkonfigurationswerte angeben, die für die Initialisierung der App über
firebase-admin, sowie den Pfad zu einer Dienstkonto-Schlüsseldatei.
So rufen Sie die Konfigurationswerte Ihres Firebase-Projekts ab:
- Öffnen Sie die Projekteinstellungen in der Firebase Konsole.
- Wählen Sie unter Meine Apps die gewünschte App aus.
Wählen Sie im rechten Bereich die Option zum Herunterladen einer Konfigurationsdatei für Apple- und Android-Apps aus.
Wählen Sie für Web-Apps Konfiguration aus, um die Konfigurationswerte anzuzeigen.
So erstellen Sie eine Schlüsseldatei:
- Öffnen Sie den Bereich Dienstkonten in der Google Cloud Console.
- Wählen Sie das App Engine Standarddienstkonto aus und wählen Sie im Optionsmenü rechts Schlüssel erstellen aus.
- Wählen Sie bei Aufforderung JSON als Schlüsseltyp aus und klicken Sie auf Erstellen.
Initialisieren Sie das SDK nach dem Speichern der Schlüsseldatei:
// At the top of test/index.test.js
// Make sure to use values from your actual Firebase configuration
const test = require('firebase-functions-test')({
databaseURL: 'https://PROJECT_ID.firebaseio.com',
storageBucket: 'PROJECT_ID.firebasestorage.app ',
projectId: 'PROJECT_ID',
}, 'path/to/serviceAccountKey.json');
SDK im Offlinemodus initialisieren
Wenn Sie vollständig offline Tests schreiben möchten, können Sie das SDK ohne Parameter initialisieren:
// At the top of test/index.test.js
const test = require('firebase-functions-test')();
Konfigurationswerte simulieren
Wenn Sie functions.config() in Ihrem Funktionscode verwenden, können Sie die Konfigurationswerte simulieren. Wenn functions/index.js beispielsweise den folgenden Code enthält:
const functions = require('firebase-functions/v1');
const key = functions.config().stripe.key;
Dann können Sie den Wert in Ihrer Testdatei so simulieren:
// Mock functions config values
test.mockConfig({ stripe: { key: '23wr42ewr34' }});
Funktionen importieren
Verwenden Sie require, um Ihre Hauptdatei mit Funktionen als
Modul zu importieren. Tun Sie dies erst, nachdem Sie firebase-functions-test,
und Konfigurationswerte simuliert haben.
// after firebase-functions-test has been initialized
const myFunctions = require('../index.js'); // relative path to functions code
Wenn Sie firebase-functions-test in
Offlinemodus initialisiert haben und admin.initializeApp() in Ihrem Funktionscode verwenden, müssen Sie es als Stub implementieren, bevor Sie
Ihre Funktionen importieren:
// If index.js calls admin.initializeApp at the top of the file, // we need to stub it out before requiring index.js. This is because the // functions will be executed as a part of the require process. // Here we stub admin.initializeApp to be a dummy function that doesn't do anything. adminInitStub = sinon.stub(admin, 'initializeApp'); // Now we can require index.js and save the exports inside a namespace called myFunctions. myFunctions = require('../index');
Hintergrundfunktionen (nicht HTTP) testen
Der Prozess zum Testen von Nicht-HTTP-Funktionen umfasst die folgenden Schritte:
- Umschließen Sie die zu testende Funktion mit der
test.wrapMethode - Testdaten erstellen
- Rufen Sie die umschlossene Funktion mit den erstellten Testdaten und allen Feldern des Ereignis kontexts auf, die Sie angeben möchten.
- Stellen Sie Behauptungen zum Verhalten auf.
Umschließen Sie zuerst die Funktion, die Sie testen möchten. Angenommen, Sie haben eine Funktion in
functions/index.js mit dem Namen makeUppercase, die Sie testen möchten. Schreiben Sie das
Folgende in functions/test/index.test.js
// "Wrap" the makeUpperCase function from index.js
const myFunctions = require('../index.js');
const wrapped = test.wrap(myFunctions.makeUppercase);
wrapped ist eine Funktion, die makeUppercase aufruft, wenn sie aufgerufen wird. wrapped
verwendet zwei Parameter:
- data (erforderlich): die Daten, die an
makeUppercasegesendet werden sollen. Dies entspricht direkt dem ersten Parameter, der an den von Ihnen geschriebenen Funktionshandler gesendet wird.firebase-functions-testbietet Methoden zum Erstellen benutzerdefinierter Daten oder Beispieldaten. - eventContextOptions (optional): Felder des Ereigniskontexts, die Sie angeben möchten. Der Ereigniskontext ist der zweite Parameter, der an den
Funktionshandler gesendet wird, den Sie geschrieben haben. Wenn Sie beim Aufrufen von
wrappedkeineneventContextOptionsParameter angeben, wird trotzdem ein Ereigniskontext mit sinnvollen Feldern generiert. Sie können einige der generierten Felder überschreiben, indem Sie sie hier angeben. Sie müssen nur die Felder angeben, die Sie überschreiben möchten. Alle Felder, die Sie nicht überschrieben haben, werden generiert.
const data = … // See next section for constructing test data
// Invoke the wrapped function without specifying the event context.
wrapped(data);
// Invoke the function, and specify params
wrapped(data, {
params: {
pushId: '234234'
}
});
// Invoke the function, and specify auth and auth Type (for real time database functions only)
wrapped(data, {
auth: {
uid: 'jckS2Q0'
},
authType: 'USER'
});
// Invoke the function, and specify all the fields that can be specified
wrapped(data, {
eventId: 'abc',
timestamp: '2018-03-23T17:27:17.099Z',
params: {
pushId: '234234'
},
auth: {
uid: 'jckS2Q0' // only for real time database functions
},
authType: 'USER' // only for real time database functions
});
Testdaten erstellen
Der erste Parameter einer umschlossenen Funktion sind die Testdaten, mit denen die zugrunde liegende Funktion aufgerufen wird. Es gibt mehrere Möglichkeiten, Testdaten zu erstellen.
Benutzerdefinierte Daten verwenden
firebase-functions-test bietet eine Reihe von Funktionen zum Erstellen von Daten, die zum Testen Ihrer Funktionen erforderlich sind. Verwenden Sie beispielsweise test.firestore.makeDocumentSnapshot
um einen Firestore DocumentSnapshotzu erstellen. Das erste Argument sind die Daten, und das
zweite Argument ist der vollständige Referenzpfad, und es gibt ein
optionales drittes Argument
für andere Eigenschaften des Snapshots, die Sie angeben können.
// Make snapshot
const snap = test.firestore.makeDocumentSnapshot({foo: 'bar'}, 'document/path');
// Call wrapped function with the snapshot
const wrapped = test.wrap(myFunctions.myFirestoreDeleteFunction);
wrapped(snap);
Wenn Sie eine onUpdate oder onWrite Funktion testen, müssen Sie
zwei Snapshots erstellen: einen für den vorherigen und einen für den nachfolgenden Zustand. Anschließend können Sie mit der makeChange Methode ein Change Objekt mit diesen Snapshots erstellen.
// Make snapshot for state of database beforehand
const beforeSnap = test.firestore.makeDocumentSnapshot({foo: 'bar'}, 'document/path');
// Make snapshot for state of database after the change
const afterSnap = test.firestore.makeDocumentSnapshot({foo: 'faz'}, 'document/path');
const change = test.makeChange(beforeSnap, afterSnap);
// Call wrapped function with the Change object
const wrapped = test.wrap(myFunctions.myFirestoreUpdateFunction);
wrapped(change);
In der API-Referenz finden Sie ähnliche Funktionen für alle anderen Datentypen.
Beispieldaten verwenden
Wenn Sie die in Ihren Tests verwendeten Daten nicht anpassen müssen, dann
firebase-functions-test bietet Methoden zum Generieren von Beispieldaten für jeden
Funktionstyp.
// For Firestore onCreate or onDelete functions
const snap = test.firestore.exampleDocumentSnapshot();
// For Firestore onUpdate or onWrite functions
const change = test.firestore.exampleDocumentSnapshotChange();
In der API-Referenz finden Sie Methoden zum Abrufen von Beispieldaten für jeden Funktionstyp.
Stubs verwenden (für den Offlinemodus)
Wenn Sie das SDK im Offlinemodus initialisiert haben und eine Cloud Firestore oder
Realtime Database Funktion testen, sollten Sie anstelle eines tatsächlichen DocumentSnapshot oder DataSnapshot ein einfaches Objekt mit Stubs
verwenden.
Angenommen, Sie schreiben einen Unit-Test für die folgende Funktion:
// Listens for new messages added to /messages/:pushId/original and creates an // uppercase version of the message to /messages/:pushId/uppercase exports.makeUppercase = functions.database.ref('/messages/{pushId}/original') .onCreate((snapshot, context) => { // Grab the current value of what was written to the Realtime Database. const original = snapshot.val(); functions.logger.log('Uppercasing', context.params.pushId, original); const uppercase = original.toUpperCase(); // You must return a Promise when performing asynchronous tasks inside a Functions such as // writing to the Firebase Realtime Database. // Setting an "uppercase" sibling in the Realtime Database returns a Promise. return snapshot.ref.parent.child('uppercase').set(uppercase); });
In der Funktion wird snap zweimal verwendet:
snap.val()snap.ref.parent.child('uppercase').set(uppercase)
Erstellen Sie im Testcode ein einfaches Objekt, in dem beide Codepfade funktionieren, und verwenden Sie Sinon, um die Methoden als Stubs zu implementieren.
// The following lines creates a fake snapshot, 'snap', which returns 'input' when snap.val() is called, // and returns true when snap.ref.parent.child('uppercase').set('INPUT') is called. const snap = { val: () => 'input', ref: { parent: { child: childStub, } } }; childStub.withArgs(childParam).returns({ set: setStub }); setStub.withArgs(setParam).returns(true);
Behauptungen aufstellen
Nachdem Sie das SDK initialisiert, die Funktionen umschlossen und Daten erstellt haben, können Sie die umschlossenen Funktionen mit den erstellten Daten aufrufen und Behauptungen zum Verhalten aufstellen. Sie können eine Bibliothek wie Chai verwenden, um diese Behauptungen aufzustellen.
Behauptungen im Onlinemodus aufstellen
Wenn Sie das Firebase Test SDK für Cloud Functions im Onlinemodus initialisiert haben, können Sie mit dem firebase-admin-SDK bestätigen, dass die gewünschten Aktionen (z. B. ein Datenbankschreibvorgang) ausgeführt wurden.
Im folgenden Beispiel wird bestätigt, dass „INPUT“ in die Datenbank des Testprojekts geschrieben wurde.
// Create a DataSnapshot with the value 'input' and the reference path 'messages/11111/original'. const snap = test.database.makeDataSnapshot('input', 'messages/11111/original'); // Wrap the makeUppercase function const wrapped = test.wrap(myFunctions.makeUppercase); // Call the wrapped function with the snapshot you constructed. return wrapped(snap).then(() => { // Read the value of the data at messages/11111/uppercase. Because `admin.initializeApp()` is // called in functions/index.js, there's already a Firebase app initialized. Otherwise, add // `admin.initializeApp()` before this line. return admin.database().ref('messages/11111/uppercase').once('value').then((createdSnap) => { // Assert that the value is the uppercased version of our input. assert.equal(createdSnap.val(), 'INPUT'); }); });
Behauptungen im Offlinemodus aufstellen
Sie können Behauptungen zum erwarteten Rückgabewert der Funktion aufstellen:
const childParam = 'uppercase'; const setParam = 'INPUT'; // Stubs are objects that fake and/or record function calls. // These are excellent for verifying that functions have been called and to validate the // parameters passed to those functions. const childStub = sinon.stub(); const setStub = sinon.stub(); // The following lines creates a fake snapshot, 'snap', which returns 'input' when snap.val() is called, // and returns true when snap.ref.parent.child('uppercase').set('INPUT') is called. const snap = { val: () => 'input', ref: { parent: { child: childStub, } } }; childStub.withArgs(childParam).returns({ set: setStub }); setStub.withArgs(setParam).returns(true); // Wrap the makeUppercase function. const wrapped = test.wrap(myFunctions.makeUppercase); // Since we've stubbed snap.ref.parent.child(childParam).set(setParam) to return true if it was // called with the parameters we expect, we assert that it indeed returned true. return wrapped(snap).then(makeUppercaseResult => { return assert.equal(makeUppercaseResult, true); });
Sie können auch Sinon-Spies verwenden, um zu bestätigen, dass bestimmte Methoden mit den erwarteten Parametern aufgerufen wurden.
HTTP-Funktionen testen
Verwenden Sie für das Testen von HTTP-onCall-Funktionen denselben Ansatz wie für das Testen von Hintergrundfunktionen.
Wenn Sie HTTP-onRequest-Funktionen testen, sollten Sie
firebase-functions-test verwenden, wenn:
- Sie
functions.config()verwenden. - Ihre Funktion mit einem Firebase-Projekt oder anderen Google APIs interagiert und Sie für Ihre Tests ein echtes Firebase-Projekt und seine Anmeldedaten verwenden möchten.
Eine HTTP-onRequest-Funktion verwendet zwei Parameter: ein Anfrageobjekt und ein Antwort
objekt. So können Sie die addMessage() Beispielfunktion testen:
- Überschreiben Sie die Umleitungsfunktion im Antwortobjekt, da sie von
sendMessage()aufgerufen wird. - Verwenden Sie in der Umleitungsfunktion chai.assert , um Behauptungen zu den Parametern aufzustellen, mit denen die Umleitungsfunktion aufgerufen werden sollte:
// A fake request object, with req.query.text set to 'input' const req = { query: {text: 'input'} }; // A fake response object, with a stubbed redirect function which asserts that it is called // with parameters 303, 'new_ref'. const res = { redirect: (code, url) => { assert.equal(code, 303); assert.equal(url, 'new_ref'); done(); } }; // Invoke addMessage with our fake request and response objects. This will cause the // assertions in the response object to be evaluated. myFunctions.addMessage(req, res);
Test bereinigen
Rufen Sie ganz am Ende Ihres Testcodes die Bereinigungsfunktion auf. Dadurch werden
Umgebungsvariablen aufgehoben, die vom SDK bei der Initialisierung festgelegt wurden, und
Firebase-Apps gelöscht, die möglicherweise erstellt wurden, wenn Sie das SDK verwendet haben, um einen Realtime
Database DataSnapshot oder Firestore DocumentSnapshot zu erstellen.
test.cleanup();
Vollständige Beispiele ansehen und weitere Informationen erhalten
Vollständige Beispiele finden Sie im Firebase-GitHub-Repository.
- Testen von Realtime Database und HTTP-Funktionen im Onlinemodus
- und HTTP-Funktionen im Offlinemodus testenRealtime Database
Weitere Informationen finden Sie in der API-Referenz
für firebase-functions-test.