Komponententests von Cloud Functions

Auf dieser Seite werden bewährte Methoden und Tools zum Schreiben von Komponententests für Ihre Funktionen beschrieben, z. B. Tests, die Teil eines Continuous Integration (CI)-Systems sind. Um das Testen zu vereinfachen, bietet Firebase das Firebase Test SDK für Cloud Functions. Es basiert auf npm als verteilte firebase-functions-test , und ist ein Begleiter Test SDK firebase-functions . Das Firebase Test SDK für Cloud Functions:

  • Kümmert sich um den entsprechenden Auf- und Abbau für Ihre Tests, wie Einstellung und Entschärfen Umgebungsvariablen benötigt durch firebase-functions .
  • Generiert Beispieldaten und Ereigniskontext, sodass Sie nur die Felder angeben müssen, die für Ihren Test relevant sind.

Versuchsaufbau

Hängen Sie beide firebase-functions-test und Mokka , ein Test - Framework, indem Sie die folgenden Befehle in Ihren Funktionen Ordner:

npm install --save-dev firebase-functions-test
npm install --save-dev mocha

Als nächstes erstellen Sie einen test eine neue Datei im Innern für Ihren Testcode Ordner innerhalb des Ordners Funktionen, erstellen und es so etwas wie nennen index.test.js .

Schließlich ändern functions/package.json folgendes hinzuzufügen:

"scripts": {
  "test": "mocha --reporter spec"
}

Sobald Sie die Tests geschrieben haben, können Sie sie , indem Sie laufen npm test in Ihrem Funktionen Verzeichnis.

Firebase Test SDK für Cloud Functions initialisieren

Es gibt zwei Möglichkeiten zu nutzen firebase-functions-test :

  1. Online - Modus (empfohlen): Write - Tests , dass interact mit einem Projekt Firebase gewidmet testen , so dass Datenbank schreibt, Benutzer erstellt usw. passieren würde , tatsächlich, und Ihr Testcode kann die Ergebnisse überprüfen. Dies bedeutet auch, dass andere Google SDKs, die in Ihren Funktionen verwendet werden, ebenfalls funktionieren.
  2. Offline - Modus: Write Silos untergebracht und Offline - Unit - Tests ohne Nebenwirkungen. Dies bedeutet, dass alle Methodenaufrufe, die mit einem Firebase-Produkt interagieren (z. B. in die Datenbank schreiben oder einen Benutzer erstellen), gestubbt werden müssen. Die Verwendung des Offlinemodus 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 Schreibtests , dass interact mit einem Testprojekt mögen, müssen Sie die Projekt - Konfigurationswerte liefern , die für die Initialisierung der App durch benötigten firebase-admin , und der Weg zu einer Dienstkonto Schlüsseldatei.

So rufen Sie die Konfigurationswerte Ihres Firebase-Projekts ab:

  1. Öffnen Sie Ihre Projekteinstellungen in der Firebase Konsole .
  2. In Ihren Apps, wählen Sie die gewünschte App.
  3. Wählen Sie im rechten Bereich die Option zum Herunterladen einer Konfigurationsdatei für iOS- und Android-Apps aus.

    Für Web - Anwendungen, wählen Sie Config Konfigurationswerte angezeigt werden soll .

So erstellen Sie eine Schlüsseldatei:

  1. Öffnen Sie die Dienstkonten Bereich der Google Cloud Console.
  2. Wählen Sie die App Engine Standarddienstkonto, und verwenden Sie das Optionsmenü auf der rechten Seite erstellen Taste auszuwählen.
  3. Wählen Sie bei Aufforderung JSON für den Schlüsseltyp, und klicken Sie auf Create.

Nachdem Sie die Schlüsseldatei gespeichert haben, initialisieren Sie 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');

SDK im Offline-Modus initialisieren

Wenn Sie komplett 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')();

Verspotten von Konfigurationswerten

Wenn Sie functions.config() in Ihren Funktionen Code können Sie die Konfigurationswerte verspotten. Wenn zum Beispiel functions/index.js enthält den folgenden Code:

const functions = require('firebase-functions');
const key = functions.config().stripe.key;

Dann können Sie den Wert in Ihrer Testdatei wie folgt vortäuschen:

// Mock functions config values
test.mockConfig({ stripe: { key: '23wr42ewr34' }});

Importieren Ihrer Funktionen

Um Ihre Funktionen zu importieren, verwenden Sie require Ihre Hauptfunktionen Datei als ein Modul zu importieren. Achten Sie darauf , nur das zu tun dies nach der Initialisierung firebase-functions-test , und spöttische Konfigurationswerte.

// after firebase-functions-test has been initialized
const myFunctions = require('../index.js'); // relative path to functions code

Wenn Sie initialisiert firebase-functions-test im Offline - admin.initializeApp() Modus , und Sie haben admin.initializeApp() in Ihren Funktionen Code, dann müssen Sie es Stummel vor Ihren Funktionen zu 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');

Testen von Hintergrundfunktionen (nicht HTTP)

Der Prozess zum Testen von Nicht-HTTP-Funktionen umfasst die folgenden Schritte:

  1. Wickeln Sie die Funktion Sie sich mit dem Test test.wrap Verfahren
  2. Testdaten erstellen
  3. Rufen Sie die umschlossene Funktion mit den von Ihnen erstellten Testdaten und allen Ereigniskontextfeldern auf, die Sie angeben möchten.
  4. Machen Sie Aussagen zum Verhalten.

Umschließen Sie zuerst die Funktion, die Sie testen möchten. Angenommen, Sie haben eine Funktion in functions/index.js genannt makeUppercase , die Sie testen möchten. Schreiben Sie die folgenden 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 aufruft makeUppercase , wenn sie aufgerufen wird. wrapped dauert 2 Parameter:

  1. Daten (erforderlich): die Daten zu senden makeUppercase . Dies entspricht direkt dem ersten Parameter, der an den von Ihnen geschriebenen Funktionshandler gesendet wird. firebase-functions-test stellt Verfahren zur Konstruktion von kundenspezifischen Daten oder beispielsweise Daten.
  2. eventContextOptions (optional): Felder des Ereignis Zusammenhang , dass Sie angeben möchten. Der Ereigniskontext ist der zweite Parameter, der an den von Ihnen geschriebenen Funktionshandler gesendet wird. Wenn Sie noch keinen umfassen eventContextOptions Parameter beim Aufruf wrapped , wird ein Ereignis Kontext noch mit vernünftigen Feldern erzeugt. 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
});

Erstellen von Testdaten

Der erste Parameter einer umschlossenen Funktion sind die Testdaten, mit denen die zugrunde liegende Funktion aufgerufen werden soll. Es gibt verschiedene Möglichkeiten, Testdaten zu erstellen.

Verwenden von benutzerdefinierten Daten

firebase-functions-test hat eine Reihe von Funktionen für die Erstellung von Daten benötigt , um Ihre Funktionen zu testen. Verwenden Sie zum Beispiel test.firestore.makeDocumentSnapshot ein Firestor erstellen DocumentSnapshot . Das erste Argument ist die Daten, und das zweite Argument ist der vollständige Referenzpfad, und es ist ein optionales drittes Argument für andere Eigenschaften des Schnappschusses 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 einen testen onUpdate oder onWrite Funktion, müssen Sie zwei Schnappschüsse erstellen: eine für den vor Staat und eine für den nach Zustand. Dann können Sie die Verwendung makeChange Methode zur Schaffung eines Change Objekt mit diesen Schnappschüssen.

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

Sehen Sie sich die API - Referenz für ähnliche Funktionen für alle anderen Datentypen.

Beispieldaten verwenden

Wenn Sie nicht die Daten in dem die Tests verwendeten anpassen müssen, dann firebase-functions-test Beispieldaten für jede Funktion Typen bieten Methoden zur Erzeugung.

// For Firestore onCreate or onDelete functions
const snap = test.firestore.exampleDocumentSnapshot();
// For Firestore onUpdate or onWrite functions
const change = test.firestore.exampleDocumentSnapshotChange();

Sehen Sie sich die API - Referenz für Methoden für für jeden Funktionstyp Beispieldaten zu bekommen.

Stubbed-Daten verwenden (für den Offline-Modus)

Wenn Sie das SDK im Offline - Modus initialisiert und testen eine Wolke Firestor oder Echtzeit - Datenbankfunktion, sollten Sie ein einfaches Objekt mit Stubs verwenden , anstatt einem tatsächlichen schaffen DocumentSnapshot oder DataSnapshot .

Angenommen, 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 snap wird zweimal verwendet:

  • snap.val()
  • snap.ref.parent.child('uppercase').set(uppercase)

In Testcode erzeugen ein einfaches Objekt , in dem diese beiden Codepfade arbeiten, und verwenden Sinon die Methoden Stummel.

// 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 konstruierten Daten aufrufen und Aussagen zum Verhalten treffen. Sie können eine Bibliothek wie verwenden Chai für die Herstellung dieser Behauptungen.

Behauptungen im Online-Modus aufstellen

Wenn Sie die Firebase - Test SDK für Cloud - Funktionen in initialisiert Online - firebase-admin Modus , können Sie behaupten , dass die gewünschten Aktionen (wie zum Beispiel eine Datenbank schreiben) stattgefunden hat , durch die Verwendung von firebase-admin SDK.

Das folgende Beispiel 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 Offline-Modus erstellen

Sie können Aussagen über den erwarteten Rückgabewert der Funktion treffen:

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 auch verwendet Sinon Spione zu behaupten , dass bestimmte Methoden aufgerufen wurden, und mit Parametern , die Sie erwarten.

HTTP-Funktionen testen

Um zu testen , HTTP ONCALL Funktionen, verwenden Sie den gleichen Ansatz wie Hintergrundfunktionen testen .

Wenn Sie testen HTTP onRequest Funktionen, die Sie verwenden sollten firebase-functions-test , wenn:

  • Sie verwenden functions.config()
  • Ihre Funktion interagiert mit einem Firebase-Projekt oder anderen Google-APIs und Sie möchten ein echtes Firebase-Projekt und dessen Anmeldedaten für Ihre Tests verwenden.

Eine HTTP-onRequest-Funktion benötigt zwei Parameter: ein Anforderungsobjekt und ein Antwortobjekt. Hier ist , wie Sie den Test könnte addMessage() Beispielfunktion :

  • Überschreiben Sie die Umleitungsfunktion in dem Antwortobjekt, da sendMessage() es nennt.
  • Innerhalb der Redirect - Funktion, verwenden chai.assert zu helfen, Aussagen über welche Parameter die Umleitung Funktion sollte mit aufgerufen werden:
// 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);

Testbereinigung

Rufen Sie ganz am Ende Ihres Testcodes die Cleanup-Funktion auf. Dieses ständig entsticht Umgebungsvariablen , dass das SDK - Set , wenn es wurde initialisiert und Firebase Apps gelöscht , die erstellt wurden, wenn Sie das SDK verwendet , um eine Echtzeit - Datenbank zu erstellen DataSnapshot oder Firestor DocumentSnapshot .

test.cleanup();

Sehen Sie sich die vollständigen Beispiele an und erfahren Sie mehr

Sie können die vollständigen Beispiele im Firebase GitHub-Repository überprüfen.

Um mehr zu erfahren, beziehen sich auf die API - firebase-functions-test Referenz für firebase-functions-test .