Firebase 将于 5 月 10 日重返 Google I/O 大会!立即报名

Testen Sie Ihre Cloud Firestore-Sicherheitsregeln

Während Sie Ihre App erstellen, möchten Sie möglicherweise den Zugriff auf Ihre Cloud Firestore-Datenbank sperren. Vor dem Start benötigen Sie jedoch differenziertere Cloud Firestore-Sicherheitsregeln. Mit dem Cloud Firestore-Emulator können Sie nicht nur Prototypen erstellen und die allgemeinen Funktionen und das Verhalten Ihrer App testen, sondern auch Einheitentests schreiben, die das Verhalten Ihrer Cloud Firestore-Sicherheitsregeln überprüfen.

Schnellstart

Probieren Sie für ein paar grundlegende Testfälle mit einfachen Regeln das Schnellstartbeispiel aus.

Cloud Firestore-Sicherheitsregeln verstehen

Implementieren Sie die Firebase-Authentifizierung und Cloud Firestore-Sicherheitsregeln für die serverlose Authentifizierung, Autorisierung und Datenvalidierung, wenn Sie die Mobil- und Web-Client-Bibliotheken verwenden.

Cloud Firestore-Sicherheitsregeln umfassen zwei Teile:

  1. Eine match Anweisung, die Dokumente in Ihrer Datenbank identifiziert.
  2. Ein allow Ausdruck, der den Zugriff auf diese Dokumente steuert.

Die Firebase-Authentifizierung überprüft die Anmeldeinformationen der Benutzer und bildet die Grundlage für benutzer- und rollenbasierte Zugriffssysteme.

Jede Datenbankanfrage von einer Cloud Firestore Mobil-/Webclientbibliothek wird anhand Ihrer Sicherheitsregeln bewertet, bevor Daten gelesen oder geschrieben werden. Wenn die Regeln den Zugriff auf einen der angegebenen Dokumentpfade verweigern, schlägt die gesamte Anforderung fehl.

Weitere Informationen zu Cloud Firestore-Sicherheitsregeln finden Sie unter Erste Schritte mit Cloud Firestore-Sicherheitsregeln .

Installieren Sie den Emulator

Um den Cloud Firestore-Emulator zu installieren, verwenden Sie die Firebase-CLI und führen Sie den folgenden Befehl aus:

firebase setup:emulators:firestore

Führen Sie den Emulator aus

Beginnen Sie mit der Initialisierung eines Firebase-Projekts in Ihrem Arbeitsverzeichnis. Dies ist ein häufiger erster Schritt bei der Verwendung der Firebase-CLI .

firebase init

Starten Sie den Emulator mit dem folgenden Befehl. Der Emulator wird ausgeführt, bis Sie den Prozess beenden:

firebase emulators:start --only firestore

In vielen Fällen möchten Sie den Emulator starten, eine Testsuite ausführen und den Emulator nach der Testausführung herunterfahren. Sie können dies einfach mit dem Befehl emulators:exec tun:

firebase emulators:exec --only firestore "./my-test-script.sh"

Beim Start versucht der Emulator, auf einem Standardport (8080) zu laufen. Sie können den Emulatorport ändern, indem Sie den Abschnitt "emulators" Ihrer Datei firebase.json “ ändern:

{
  // ...
  "emulators": {
    "firestore": {
      "port": "YOUR_PORT"
    }
  }
}

Bevor Sie den Emulator ausführen

Beachten Sie Folgendes, bevor Sie mit der Verwendung des Emulators beginnen:

  • Der Emulator lädt zunächst die Regeln, die im Feld firestore.rules Ihrer Datei firebase.json angegeben sind. Es erwartet den Namen einer lokalen Datei, die Ihre Cloud Firestore-Sicherheitsregeln enthält, und wendet diese Regeln auf alle Projekte an. Wenn Sie den lokalen Dateipfad nicht angeben oder die Methode loadFirestoreRules wie unten beschrieben verwenden, behandelt der Emulator alle Projekte so, als hätten sie offene Regeln.
  • Während die meisten Firebase-SDKs direkt mit den Emulatoren arbeiten, unterstützt nur die @firebase/rules-unit-testing Bibliothek das Mocking auth in Security Rules, wodurch Komponententests viel einfacher werden. Darüber hinaus unterstützt die Bibliothek einige emulatorspezifische Funktionen wie das Löschen aller Daten, wie unten aufgeführt.
  • Die Emulatoren akzeptieren auch Produktions-Firebase-Authentifizierungstoken, die über Client-SDKs bereitgestellt werden, und werten die Regeln entsprechend aus, sodass Ihre Anwendung in Integrations- und manuellen Tests direkt mit den Emulatoren verbunden werden kann.

Führen Sie lokale Einheitentests durch

Führen Sie lokale Komponententests mit dem v9 JavaScript SDK aus

Firebase vertreibt eine Unit-Testing-Bibliothek für Sicherheitsregeln sowohl mit dem JavaScript-SDK der Version 9 als auch mit dem SDK der Version 8. Die Bibliotheks-APIs unterscheiden sich erheblich. Wir empfehlen die v9-Testbibliothek, die optimierter ist und weniger Setup erfordert, um eine Verbindung zu Emulatoren herzustellen und so die versehentliche Verwendung von Produktionsressourcen sicher zu vermeiden. Aus Gründen der Abwärtskompatibilität stellen wir weiterhin die v8-Testbibliothek zur Verfügung .

Verwenden Sie das Modul @firebase/rules-unit-testing um mit dem lokal ausgeführten Emulator zu interagieren. Wenn Sie Zeitüberschreitungen oder ECONNREFUSED Fehler erhalten, überprüfen Sie, ob der Emulator tatsächlich ausgeführt wird.

Wir empfehlen dringend, eine aktuelle Version von Node.js zu verwenden, damit Sie async/await -Notation verwenden können. Fast das gesamte Verhalten, das Sie möglicherweise testen möchten, umfasst asynchrone Funktionen, und das Testmodul ist so konzipiert, dass es mit Promise-basiertem Code funktioniert.

Die v9 Rules Unit Testing-Bibliothek kennt die Emulatoren immer und berührt niemals Ihre Produktionsressourcen.

Sie importieren die Bibliothek mit modularen v9-Importanweisungen. Zum Beispiel:

import {
  assertFails,
  assertSucceeds,
  initializeTestEnvironment,
  RulesTestEnvironment,
} from "@firebase/rules-unit-testing"

// Use `const { … } = require("@firebase/rules-unit-testing")` if imports are not supported
// Or we suggest `const testing = require("@firebase/rules-unit-testing")` if necessary.

Nach dem Import umfasst die Implementierung von Unit-Tests Folgendes:

  • Erstellen und Konfigurieren einer RulesTestEnvironment mit einem Aufruf von initializeTestEnvironment .
  • Einrichten von Testdaten ohne Auslösen von Regeln mithilfe einer bequemen Methode, mit der Sie sie vorübergehend umgehen können, RulesTestEnvironment.withSecurityRulesDisabled .
  • Einrichten von Test-Suite und Pro-Test-Vorher/Nachher-Hooks mit Aufrufen zum Bereinigen von Testdaten und -umgebung, wie RulesTestEnvironment.cleanup() oder RulesTestEnvironment.clearFirestore() .
  • Implementieren von Testfällen, die Authentifizierungszustände mit RulesTestEnvironment.authenticatedContext und RulesTestEnvironment.unauthenticatedContext nachahmen.

Gängige Methoden und Hilfsfunktionen

Siehe auch emulatorspezifische Testmethoden im v9 SDK .

initializeTestEnvironment() => RulesTestEnvironment

Diese Funktion initialisiert eine Testumgebung für das Testen von Regeleinheiten. Rufen Sie diese Funktion zuerst für den Testaufbau auf. Für eine erfolgreiche Ausführung müssen Emulatoren ausgeführt werden.

Die Funktion akzeptiert ein optionales Objekt, das eine TestEnvironmentConfig definiert, die aus einer Projekt-ID und Emulatorkonfigurationseinstellungen bestehen kann.

let testEnv = await initializeTestEnvironment({
  projectId: "demo-project-1234",
  firestore: {
    rules: fs.readFileSync("firestore.rules", "utf8"),
  },
});

RulesTestEnvironment.authenticatedContext({ user_id: string, tokenOptions?: TokenOptions }) => RulesTestContext

Diese Methode erstellt einen RulesTestContext , der sich wie ein authentifizierter Authentifizierungsbenutzer verhält. Anfragen, die über den zurückgegebenen Kontext erstellt werden, haben ein Schein-Authentifizierungstoken angehängt. Übergeben Sie optional ein Objekt, das benutzerdefinierte Ansprüche oder Außerkraftsetzungen für Authentifizierungstoken-Nutzlasten definiert.

Verwenden Sie das zurückgegebene Testkontextobjekt in Ihren Tests, um auf alle konfigurierten Emulatorinstanzen zuzugreifen, einschließlich der mit initializeTestEnvironment konfigurierten.

// Assuming a Firestore app and the Firestore emulator for this example
import { setDoc } from "firebase/firestore";

const alice = testEnv.authenticatedContext("alice", { … });
// Use the Firestore instance associated with this context
await assertSucceeds(setDoc(alice.firestore(), '/users/alice'), { ... });

RulesTestEnvironment.unauthenticatedContext() => RulesTestContext

Diese Methode erstellt einen RulesTestContext , der sich wie ein Client verhält, der nicht per Authentifizierung angemeldet ist. Anforderungen, die über den zurückgegebenen Kontext erstellt wurden, sind keine Firebase-Authentifizierungstoken angehängt.

Verwenden Sie das zurückgegebene Testkontextobjekt in Ihren Tests, um auf alle konfigurierten Emulatorinstanzen zuzugreifen, einschließlich der mit initializeTestEnvironment konfigurierten.

// Assuming a Cloud Storage app and the Storage emulator for this example
import { getStorage, ref, deleteObject } from "firebase/storage";

const alice = testEnv.unauthenticatedContext();

// Use the Cloud Storage instance associated with this context
const desertRef = ref(alice.storage(), 'images/desert.jpg');
await assertSucceeds(deleteObject(desertRef));

RulesTestEnvironment.withSecurityRulesDisabled()

Führen Sie eine Testeinrichtungsfunktion mit einem Kontext aus, der sich so verhält, als wären Sicherheitsregeln deaktiviert.

Diese Methode verwendet eine Callback-Funktion, die den Sicherheitsregeln umgehenden Kontext übernimmt und ein Versprechen zurückgibt. Der Kontext wird zerstört, sobald das Promise aufgelöst/zurückgewiesen wird.

RulesTestEnvironment.cleanup()

Diese Methode zerstört alle RulesTestContexts , die in der Testumgebung erstellt wurden, und bereinigt die zugrunde liegenden Ressourcen, wodurch ein sauberes Beenden ermöglicht wird.

Diese Methode ändert den Status von Emulatoren in keiner Weise. Um Daten zwischen Tests zurückzusetzen, verwenden Sie die anwendungsemulatorspezifische Methode zum Löschen von Daten.

assertSucceeds(pr: Promise<any>)) => Promise<any>

Dies ist eine Testfall-Utility-Funktion.

Die Funktion bestätigt, dass das bereitgestellte Versprechen, das einen Emulatorvorgang umschließt, ohne Verletzungen der Sicherheitsregeln aufgelöst wird.

await assertSucceeds(setDoc(alice.firestore(), '/users/alice'), { ... });

assertFails(pr: Promise<any>)) => Promise<any>

Dies ist eine Testfall-Utility-Funktion.

Die Funktion bestätigt, dass das bereitgestellte Promise Wrapping einer Emulatoroperation mit einer Verletzung der Sicherheitsregeln abgelehnt wird.

await assertFails(setDoc(alice.firestore(), '/users/bob'), { ... });

Emulatorspezifische Methoden

Siehe auch allgemeine Testmethoden und Hilfsfunktionen im v9 SDK .

RulesTestEnvironment.clearFirestore() => Promise<void>

Diese Methode löscht Daten in der Firestore-Datenbank, die zu der für den Firestore-Emulator konfigurierten projectId gehören.

RulesTestContext.firestore(settings?: Firestore.FirestoreSettings) => Firestore;

Diese Methode ruft eine Firestore-Instanz für diesen Testkontext ab. Die zurückgegebene Firebase JS-Client-SDK-Instanz kann mit den Client-SDK-APIs (v9 modular oder v9 kompatibel) verwendet werden.

Regelauswertungen visualisieren

Mit dem Cloud Firestore-Emulator können Sie Clientanfragen in der Benutzeroberfläche der Emulator Suite visualisieren, einschließlich Auswertungstrace für Firebase-Sicherheitsregeln.

Öffnen Sie die Registerkarte Firestore > Anfragen , um die detaillierte Bewertungssequenz für jede Anfrage anzuzeigen.

Firestore Emulator Requests Monitor mit Auswertungen der Sicherheitsregeln

Prüfberichte erstellen

Nachdem Sie eine Reihe von Tests ausgeführt haben, können Sie auf Testabdeckungsberichte zugreifen, die zeigen, wie jede Ihrer Sicherheitsregeln bewertet wurde.

Um die Berichte abzurufen, fragen Sie einen exponierten Endpunkt auf dem Emulator ab, während er ausgeführt wird. Verwenden Sie für eine browserfreundliche Version die folgende URL:

http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage.html

Dadurch werden Ihre Regeln in Ausdrücke und Unterausdrücke unterteilt, über die Sie mit der Maus fahren können, um weitere Informationen anzuzeigen, einschließlich der Anzahl der zurückgegebenen Auswertungen und Werte. Für die rohe JSON-Version dieser Daten fügen Sie die folgende URL in Ihre Abfrage ein:

http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage

Unterschiede zwischen Emulator und Produktion

  1. Sie müssen kein Cloud Firestore-Projekt explizit erstellen. Der Emulator erstellt automatisch jede Instanz, auf die zugegriffen wird.
  2. Der Cloud Firestore-Emulator funktioniert nicht mit dem normalen Firebase-Authentifizierungsablauf. Stattdessen haben wir im Firebase Test SDK die initializeTestApp() Methode in der rules-unit-testing Bibliothek bereitgestellt, die ein auth akzeptiert. Das mit dieser Methode erstellte Firebase-Handle verhält sich so, als hätte es sich erfolgreich als die von Ihnen bereitgestellte Entität authentifiziert. Wenn Sie null übergeben, verhält es sich wie ein nicht authentifizierter Benutzer ( auth != null -Regeln schlagen beispielsweise fehl).

Beheben Sie bekannte Probleme

Bei der Verwendung des Cloud Firestore-Emulators können die folgenden bekannten Probleme auftreten. Befolgen Sie die nachstehenden Anweisungen, um Probleme mit unregelmäßigem Verhalten zu beheben. Diese Hinweise wurden unter Berücksichtigung der Einheitentestbibliothek für Sicherheitsregeln geschrieben, aber die allgemeinen Ansätze gelten für jedes Firebase-SDK.

Das Testverhalten ist inkonsistent

Wenn Ihre Tests gelegentlich bestanden und fehlgeschlagen werden, auch ohne Änderungen an den Tests selbst, müssen Sie möglicherweise überprüfen, ob sie ordnungsgemäß sequenziert sind. Die meisten Interaktionen mit dem Emulator sind asynchron, also überprüfen Sie noch einmal, ob der gesamte asynchrone Code richtig sequenziert ist. Sie können die Reihenfolge festlegen, indem Sie entweder Promises verketten oder großzügig await Notation verwenden.

Überprüfen Sie insbesondere die folgenden asynchronen Vorgänge:

  • Festlegen von Sicherheitsregeln, beispielsweise mit initializeTestEnvironment .
  • Lesen und Schreiben von Daten, zum Beispiel mit db.collection("users").doc("alice").get() .
  • Operative Zusicherungen, einschließlich assertSucceeds und assertFails .

Tests bestehen nur, wenn Sie den Emulator zum ersten Mal laden

Der Emulator ist zustandsbehaftet. Es speichert alle darauf geschriebenen Daten im Speicher, sodass alle Daten verloren gehen, wenn der Emulator heruntergefahren wird. Wenn Sie mehrere Tests mit derselben Projekt-ID ausführen, kann jeder Test Daten erzeugen, die nachfolgende Tests beeinflussen können. Sie können eine der folgenden Methoden verwenden, um dieses Verhalten zu umgehen:

  • Verwenden Sie für jeden Test eindeutige Projekt-IDs. Beachten Sie, dass Sie in diesem Fall initializeTestEnvironment als Teil jedes Tests aufrufen müssen; Regeln werden nur für die Standard-Projekt-ID automatisch geladen.
  • Restrukturieren Sie Ihre Tests, sodass sie nicht mit zuvor geschriebenen Daten interagieren (verwenden Sie beispielsweise für jeden Test eine andere Sammlung).
  • Löschen Sie alle während eines Tests geschriebenen Daten.

Der Testaufbau ist sehr kompliziert

Beim Einrichten Ihres Tests möchten Sie möglicherweise Daten auf eine Weise ändern, die Ihre Cloud Firestore-Sicherheitsregeln eigentlich nicht zulassen. Wenn Ihre Regeln die Testeinrichtung komplex machen, versuchen Sie, RulesTestEnvironment.withSecurityRulesDisabled in Ihren Einrichtungsschritten zu verwenden, damit Lese- und Schreibvorgänge keine PERMISSION_DENIED -Fehler auslösen.

Danach kann Ihr Test Vorgänge als authentifizierter oder nicht authentifizierter Benutzer mit RulesTestEnvironment.authenticatedContext bzw. unauthenticatedContext ausführen. Auf diese Weise können Sie überprüfen, ob Ihre Cloud Firestore-Sicherheitsregeln verschiedene Fälle korrekt zulassen/verweigern.