Auf dieser Seite werden Best Practices und Tools zum Schreiben von Unit-Tests für Ihre Funktionen beschrieben, z. B. Tests, die Teil eines Continuous Integration (CI)-Systems wären. Um das Testen zu vereinfachen, stellt Firebase das Firebase Test SDK für Cloud Functions bereit. 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:
- Kümmert sich um die entsprechende Einrichtung und Demontage Ihrer Tests, z. B. das Setzen und Deaktivieren von Umgebungsvariablen, die von
firebase-functions
benötigt werden. - Generiert Beispieldaten und Ereigniskontext, sodass Sie nur die Felder angeben müssen, die für Ihren Test relevant sind.
Versuchsaufbau
Installieren Sie sowohl firebase-functions-test
als auch Mocha , ein Testframework, indem Sie die folgenden Befehle in Ihrem Funktionsordner ausführen:
npm install --save-dev firebase-functions-test
npm install --save-dev mocha
Erstellen Sie als Nächstes einen test
im Funktionsordner, erstellen Sie darin eine neue Datei für Ihren Testcode und nennen Sie sie etwa index.test.js
.
Ändern Sie abschließend functions/package.json
, um Folgendes hinzuzufügen:
"scripts": {
"test": "mocha --reporter spec"
}
Sobald Sie die Tests geschrieben haben, können Sie sie ausführen, indem Sie npm test
in Ihrem Funktionsverzeichnis ausführen.
Initialisierung des Firebase Test SDK für Cloud Functions
Es gibt zwei Möglichkeiten, firebase-functions-test
zu verwenden:
- Online-Modus (empfohlen): Schreiben Sie Tests, die mit einem Firebase-Projekt interagieren, das dem Testen gewidmet ist, sodass Datenbankschreibvorgänge, Benutzererstellungen usw. tatsächlich stattfinden und Ihr Testcode die Ergebnisse überprüfen kann. Dies bedeutet auch, dass andere in Ihren Funktionen verwendete Google SDKs ebenfalls funktionieren.
- Offline-Modus: Schreiben Sie isolierte und Offline-Komponententests ohne Nebenwirkungen. Das bedeutet, dass alle Methodenaufrufe, die mit einem Firebase-Produkt interagieren (z. B. in die Datenbank schreiben oder einen Benutzer erstellen), blockiert werden müssen. Die Verwendung des Offline-Modus wird im Allgemeinen nicht empfohlen, wenn Sie über Cloud Firestore- oder Realtime Database-Funktionen verfügen, da dies die Komplexität Ihres Testcodes erheblich erhöht.
SDK im Online-Modus initialisieren (empfohlen)
Wenn Sie Tests schreiben möchten, die mit einem Testprojekt interagieren, müssen Sie die Projektkonfigurationswerte angeben, die zum Initialisieren der App über firebase-admin
erforderlich sind, sowie den Pfad zu einer Dienstkontoschlüsseldatei.
So erhalten Sie die Konfigurationswerte Ihres Firebase-Projekts:
- Öffnen Sie Ihre Projekteinstellungen in der Firebase-Konsole .
- Wählen Sie unter Ihre 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 „Config“ aus, um Konfigurationswerte anzuzeigen.
So erstellen Sie eine Schlüsseldatei:
- Öffnen Sie den Bereich „Dienstkonten“ der Google Cloud Console.
- Wählen Sie das App Engine-Standarddienstkonto aus und wählen Sie im Optionsmenü rechts die Option Schlüssel erstellen aus.
- Wenn Sie dazu aufgefordert werden, wählen Sie JSON als Schlüsseltyp aus und klicken Sie auf Erstellen .
Initialisieren Sie nach dem Speichern der Schlüsseldatei das SDK:
// At the top of test/index.test.js
const test = require('firebase-functions-test')({
databaseURL: 'https://my-project.firebaseio.com',
storageBucket: 'my-project.appspot.com',
projectId: 'my-project',
}, 'path/to/serviceAccountKey.json');
Initialisieren Sie das SDK im Offline-Modus
Wenn Sie vollständige 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 verspotten
Wenn Sie functions.config()
in Ihrem Funktionscode verwenden, können Sie die Konfigurationswerte verspotten. Wenn zum Beispiel functions/index.js
den folgenden Code enthält:
const functions = require('firebase-functions');
const key = functions.config().stripe.key;
Dann können Sie den Wert in Ihrer Testdatei wie folgt verspotten:
// Mock functions config values
test.mockConfig({ stripe: { key: '23wr42ewr34' }});
Importieren Sie Ihre Funktionen
Um Ihre Funktionen zu importieren, verwenden Sie require
, um Ihre Hauptfunktionsdatei als Modul zu importieren. Stellen Sie sicher, dass Sie dies erst tun, nachdem Sie firebase-functions-test
initialisiert und Konfigurationswerte verspottet haben.
// after firebase-functions-test has been initialized
const myFunctions = require('../index.js'); // relative path to functions code
Wenn Sie firebase-functions-test
im Offline-Modus initialisiert haben und admin.initializeApp()
in Ihrem Funktionscode enthalten ist, müssen Sie es vor dem Importieren Ihrer Funktionen stubben:
// 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 Funktion, die Sie testen möchten, mit der Methode
test.wrap
- Testdaten erstellen
- Rufen Sie die umschlossene Funktion mit den von Ihnen erstellten Testdaten und allen Ereigniskontextfeldern auf, die Sie angeben möchten.
- Machen Sie Aussagen über das Verhalten.
Umschließen Sie zunächst die Funktion, die Sie testen möchten. Nehmen wir an, Sie haben in functions/index.js
eine Funktion namens makeUppercase
“, die Sie testen möchten. Schreiben Sie Folgendes 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 beim Aufruf makeUppercase
aufruft. wrapped
benötigt zwei Parameter:
- data (erforderlich): die Daten, die an
makeUppercase
gesendet werden sollen. Dies entspricht direkt dem ersten Parameter, der an den von Ihnen geschriebenen Funktionshandler gesendet wird.firebase-functions-test
bietet Methoden zum Erstellen benutzerdefinierter Daten oder Beispieldaten. - eventContextOptions (optional): Felder des Ereigniskontexts, den Sie angeben möchten. Der Ereigniskontext ist der zweite Parameter, der an den von Ihnen geschriebenen Funktionshandler gesendet wird. Wenn Sie beim Aufruf von
wrapped
keineneventContextOptions
Parameter angeben, wird dennoch ein Ereigniskontext mit sinnvollen Feldern generiert. Sie können einige der generierten Felder überschreiben, indem Sie sie hier angeben. Beachten Sie, dass Sie nur die Felder einschließen müssen, 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 verschiedene Möglichkeiten, Testdaten zu erstellen.
Verwendung benutzerdefinierter Daten
firebase-functions-test
verfügt über eine Reihe von Funktionen zum Erstellen von Daten, die zum Testen Ihrer Funktionen erforderlich sind. Verwenden Sie beispielsweise test.firestore.makeDocumentSnapshot
, um einen Firestore DocumentSnapshot
zu 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 Vorher-Zustand und einen für den Nachher-Zustand. Anschließend können Sie mit der Methode makeChange
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);
Ähnliche Funktionen für alle anderen Datentypen finden Sie in der API-Referenz .
Anhand von Beispieldaten
Wenn Sie die in Ihren Tests verwendeten Daten nicht anpassen müssen, bietet firebase-functions-test
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();
Methoden zum Abrufen von Beispieldaten für jeden Funktionstyp finden Sie in der API-Referenz .
Verwenden von Stub-Daten (für den Offline-Modus)
Wenn Sie das SDK im Offline-Modus initialisiert haben und eine Cloud Firestore- oder Realtime Database-Funktion testen, sollten Sie ein einfaches Objekt mit Stubs verwenden, anstatt einen tatsächlichen DocumentSnapshot
oder DataSnapshot
zu erstellen.
Nehmen wir an, Sie schreiben einen Komponententest 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); });
Innerhalb 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 zu unterbrechen.
// 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 verpackt und Daten erstellt haben, können Sie die verpackten Funktionen mit den erstellten Daten aufrufen und Aussagen zum Verhalten treffen. Sie können eine Bibliothek wie Chai verwenden, um diese Behauptungen aufzustellen.
Aussagen im Online-Modus treffen
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) stattgefunden haben.
Im folgenden Beispiel wird behauptet, 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'); }); });
Aussagen im Offline-Modus treffen
Sie können Aussagen über den erwarteten Rückgabewert der Funktion machen:
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 assert.equal(wrapped(snap), true);
Sie können Sinon-Spione auch verwenden, um sicherzustellen, dass bestimmte Methoden aufgerufen wurden, und zwar mit den von Ihnen erwarteten Parametern.
Testen von HTTP-Funktionen
Um HTTP-onCall-Funktionen zu testen, verwenden Sie denselben Ansatz wie beim Testen von Hintergrundfunktionen .
Wenn Sie HTTP-onRequest-Funktionen testen, sollten Sie firebase-functions-test
verwenden, wenn:
- Sie verwenden
functions.config()
- Ihre Funktion interagiert mit einem Firebase-Projekt oder anderen Google-APIs und Sie möchten für Ihre Tests ein echtes Firebase-Projekt und seine Anmeldeinformationen verwenden.
Eine HTTP-onRequest-Funktion benötigt zwei Parameter: ein Anforderungsobjekt und ein Antwortobjekt. So können Sie die Beispielfunktion addMessage()
testen:
- Überschreiben Sie die Umleitungsfunktion im Antwortobjekt, da
sendMessage()
sie aufruft. - Verwenden Sie innerhalb der Umleitungsfunktion chai.assert , um Aussagen darüber zu treffen, mit welchen Parametern die Umleitungsfunktion aufgerufen werden soll:
// 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);
Probereinigung
Rufen Sie ganz am Ende Ihres Testcodes die Bereinigungsfunktion auf. Dadurch werden Umgebungsvariablen zurückgesetzt, die das SDK bei der Initialisierung festgelegt hat, und Firebase-Apps werden gelöscht, die möglicherweise erstellt wurden, wenn Sie das SDK zum Erstellen einer Echtzeitdatenbank DataSnapshot
oder Firestore DocumentSnapshot
verwendet haben.
test.cleanup();
Sehen Sie sich vollständige Beispiele an und erfahren Sie mehr
Sie können die vollständigen Beispiele im Firebase-GitHub-Repository einsehen.
- Testen von Echtzeitdatenbank- und HTTP-Funktionen im Online-Modus
- Testen von Echtzeitdatenbank- und HTTP-Funktionen im Offline-Modus
Weitere Informationen finden Sie in der API-Referenz für firebase-functions-test
.