Restez organisé à l'aide des collections Enregistrez et classez les contenus selon vos préférences.

Tester les règles de sécurité

Lors de la création de votre application, vous souhaiterez peut-être verrouiller l'accès à votre base de données Firestore. Toutefois, avant le lancement, vous aurez besoin de règles de sécurité Firestore plus nuancées. Avec l'émulateur Firestore, en plus du prototypage et du test des fonctionnalités et des comportements généraux de votre application, vous pouvez écrire des tests unitaires qui vérifient le comportement de vos règles de sécurité Firestore.

Guide de démarrage rapide

Pour quelques scénarios de test de base avec des règles simples, consultez l'exemple de démarrage rapide.

Comprendre les règles de sécurité de Firestore

Mettez en œuvre Firebase Authentication et les règles de sécurité Firestore pour l'authentification, l'autorisation et la validation des données sans serveur lorsque vous utilisez les bibliothèques clientes mobiles et Web.

Les règles de sécurité de Firestore comprennent deux éléments:

  1. une instruction match qui identifie les documents de votre base de données ;
  2. Expression allow qui contrôle l'accès à ces documents.

Firebase Authentication vérifie les identifiants des utilisateurs et fournit la base des systèmes d'accès basés sur l'utilisateur et sur les rôles.

Chaque requête de base de données provenant d'une bibliothèque cliente mobile/Web Firestore est évaluée par rapport à vos règles de sécurité avant de lire ou d'écrire des données. Si les règles refusent l'accès à l'un des chemins d'accès de document spécifiés, la requête entière échoue.

Pour en savoir plus sur les règles de sécurité Firestore, consultez la page Premiers pas avec les règles de sécurité Firestore.

Installer l'émulateur

Pour installer l'émulateur Firestore, utilisez la CLI Firebase et exécutez la commande ci-dessous:

firebase setup:emulators:firestore

Exécuter l'émulateur

Commencez par initialiser un projet Firebase dans votre répertoire de travail. Il s'agit d'une première étape courante lorsque vous utilisez la CLI Firebase.

firebase init

Démarrez l'émulateur à l'aide de la commande suivante. L'émulateur s'exécute jusqu'à la fin du processus:

firebase emulators:start --only firestore

Dans de nombreux cas, vous souhaitez démarrer l'émulateur, exécuter une suite de tests, puis l'arrêter après l'exécution des tests. Pour ce faire, utilisez la commande emulators:exec:

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

Au démarrage, l'émulateur tente de s'exécuter sur un port par défaut (8080). Vous pouvez changer le port de l'émulateur en modifiant la section "emulators" de votre fichier firebase.json:

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

Avant d'exécuter l'émulateur

Avant de commencer à utiliser l'émulateur, tenez compte des points suivants:

  • L'émulateur charge initialement les règles spécifiées dans le champ firestore.rules de votre fichier firebase.json. Il attend le nom d'un fichier local contenant vos règles de sécurité Firestore et applique ces règles à tous les projets. Si vous ne fournissez pas le chemin d'accès au fichier local ou utilisez la méthode loadFirestoreRules comme décrit ci-dessous, l'émulateur traite tous les projets comme ayant des règles ouvertes.
  • Bien que la plupart des SDK Firebase fonctionnent directement avec les émulateurs, seule la bibliothèque @firebase/rules-unit-testing est compatible avec la simulation auth dans les règles de sécurité, ce qui facilite grandement les tests unitaires. En outre, la bibliothèque est compatible avec quelques fonctionnalités spécifiques à l'émulateur, telles que la suppression de toutes les données, comme indiqué ci-dessous.
  • Les émulateurs accepteront également les jetons d'authentification Firebase de production fournis via les SDK client et évalueront les règles en conséquence. Vous pourrez ainsi connecter directement votre application aux émulateurs lors des tests d'intégration et manuels.

Exécuter des tests unitaires locaux

Exécuter des tests unitaires locaux avec le SDK JavaScript v9

Firebase distribue une bibliothèque de tests unitaires de règles de sécurité avec son SDK JavaScript version 9 et son SDK version 8. Les API des bibliothèques sont très différentes. Nous recommandons la bibliothèque de tests v9, qui est plus simplifiée et nécessite moins de configuration pour se connecter aux émulateurs. Elle évite ainsi toute utilisation accidentelle des ressources de production. Pour assurer la rétrocompatibilité, nous continuons à rendre la bibliothèque de test v8 disponible.

Utilisez le module @firebase/rules-unit-testing pour interagir avec l'émulateur qui s'exécute localement. Si vous obtenez des délais avant expiration ou des erreurs ECONNREFUSED, vérifiez que l'émulateur est bien en cours d'exécution.

Nous vous recommandons vivement d'utiliser une version récente de Node.js afin de pouvoir utiliser la notation async/await. La quasi-totalité du comportement à tester implique des fonctions asynchrones. Le module de test est conçu pour fonctionner avec du code basé sur la promesse.

La bibliothèque de tests unitaires des règles de la version 9 tient compte des émulateurs et ne modifie jamais vos ressources de production.

Importez la bibliothèque à l'aide d'instructions d'importation modulaire v9. Exemple:

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.

Une fois l'importation effectuée, la mise en œuvre de tests unitaires implique les opérations suivantes:

  • Créer et configurer un RulesTestEnvironment avec un appel à initializeTestEnvironment
  • Configurer des données de test sans déclencher des règles, à l'aide d'une méthode pratique permettant de les contourner temporairement, RulesTestEnvironment.withSecurityRulesDisabled
  • Configurer des suites de tests et des hooks avant/après avec des appels permettant de nettoyer les données et l'environnement de test, comme RulesTestEnvironment.cleanup() ou RulesTestEnvironment.clearFirestore().
  • Mettre en œuvre des scénarios de test qui imitent les états d'authentification à l'aide de RulesTestEnvironment.authenticatedContext et de RulesTestEnvironment.unauthenticatedContext

Méthodes courantes et fonctions utilitaires

Consultez également la documentation sur les méthodes de test spécifiques à l'émulateur dans le SDK v9.

initializeTestEnvironment() => RulesTestEnvironment

Cette fonction initialise un environnement de test pour réaliser des tests unitaires de règles. Appelez d'abord cette fonction pour la configuration du test. L'exécution réussie nécessite l'exécution d'émulateurs.

La fonction accepte un objet facultatif définissant une TestEnvironmentConfig, qui peut être constituée d'un ID de projet et des paramètres de configuration de l'émulateur.

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

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

Cette méthode crée un RulesTestContext qui agit comme un utilisateur authentifié. Les requêtes créées via le contexte renvoyé comportent un jeton d'authentification fictif associé. Si vous le souhaitez, transmettez un objet définissant les revendications personnalisées ou les remplacements pour les charges utiles de jeton d'authentification.

Utilisez l'objet de contexte de test renvoyé dans vos tests pour accéder aux instances d'émulateur configurées, y compris celles configurées avec initializeTestEnvironment.

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

Cette méthode crée un RulesTestContext qui agit comme un client qui n'est pas connecté via l'authentification. Les requêtes créées via le contexte renvoyé ne comportent pas de jetons d'authentification Firebase.

Utilisez l'objet de contexte de test renvoyé dans vos tests pour accéder aux instances d'émulateur configurées, y compris celles configurées avec initializeTestEnvironment.

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

Exécutez une fonction de configuration test avec un contexte qui se comporte comme si les règles de sécurité étaient désactivées.

Cette méthode utilise une fonction de rappel, qui utilise le contexte de contournement des règles de sécurité et renvoie une promesse. Le contexte sera détruit une fois que la promesse aura été résolue / refusée.

RulesTestEnvironment.cleanup()

Cette méthode détruit tous les RulesTestContexts créés dans l'environnement de test et nettoie les ressources sous-jacentes, ce qui permet une sortie soignée.

Cette méthode ne modifie en aucune façon l'état des émulateurs. Pour réinitialiser les données entre les tests, utilisez la méthode de suppression de données spécifique à l'émulateur d'application.

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

Il s'agit d'une fonction utilitaire du scénario de test.

La fonction affirme que la promesse fournie encapsulant une opération de l'émulateur sera résolue sans violation des règles de sécurité.

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

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

Il s'agit d'une fonction utilitaire du scénario de test.

La fonction affirme que la promesse fournie encapsulant une opération de l'émulateur sera rejetée avec une violation des règles de sécurité.

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

Méthodes spécifiques à l'émulateur

Consultez également la section Méthodes de test courantes et fonctions utilitaires du SDK v9.

RulesTestEnvironment.clearFirestore() => Promise<void>

Cette méthode efface les données de la base de données Firestore appartenant à l'instance projectId configurée pour l'émulateur Firestore.

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

Cette méthode obtient une instance Firestore pour ce contexte de test. L'instance renvoyée du SDK client Firebase JS peut être utilisée avec les API SDK client (v9 modulaire ou v9 compat).

Visualiser les évaluations de règles

L'émulateur Firestore vous permet de visualiser les requêtes des clients dans l'interface utilisateur de la suite d'émulateurs, y compris le traçage d'évaluation pour les règles de sécurité Firebase.

Ouvrez l'onglet Requêtes Firestore pour afficher la séquence d'évaluation détaillée de chaque requête.

Requêtes de l&#39;émulateur Firestore affichant les évaluations des règles de sécurité

Générer des rapports de test

Après avoir exécuté une suite de tests, vous pouvez accéder aux rapports de couverture des tests qui indiquent comment chacune de vos règles de sécurité a été évaluée.

Pour obtenir les rapports, interrogez un point de terminaison exposé sur l'émulateur pendant l'exécution de l'outil. Pour une version compatible avec le navigateur, utilisez l'URL suivante:

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

Cela divise vos règles en expressions et sous-expressions que vous pouvez placer à la recherche pour plus d'informations, y compris le nombre d'évaluations et de valeurs renvoyées. Pour la version JSON brute de ces données, incluez l'URL suivante dans votre requête:

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

Différences entre l'émulateur et la production

  1. Vous n'avez pas besoin de créer explicitement un projet Firestore. L'émulateur crée automatiquement toute instance accessible.
  2. L'émulateur Firestore ne fonctionne pas avec le flux normal Firebase Authentication. À la place, dans le SDK de test Firebase, nous avons fourni la méthode initializeTestApp() dans la bibliothèque rules-unit-testing, qui utilise un champ auth. Le handle Firebase créé à l'aide de cette méthode se comportera comme s'il avait été authentifié comme n'importe quelle entité que vous fournissez. Si vous transmettez null, il se comportera comme un utilisateur non authentifié (les règles auth != null échoueront, par exemple).

Résoudre les problèmes connus

Lorsque vous utilisez l'émulateur Firestore, vous pouvez rencontrer les problèmes connus suivants. Suivez les conseils ci-dessous pour résoudre les problèmes de comportement irrégulier. Ces notes sont conçues pour la bibliothèque de tests unitaires des règles de sécurité, mais les approches générales s'appliquent à tous les SDK Firebase.

Comportement de test incohérent

Si vos tests réussissent et échouent parfois, même si vous n'apportez aucune modification, vous devrez peut-être vérifier qu'ils sont correctement séquencés. La plupart des interactions avec l'émulateur sont asynchrones. Vous devez donc vérifier que tout le code asynchrone est correctement séquencé. Vous pouvez corriger le séquençage en enchaînant les promesses ou en utilisant librement la notation await.

En particulier, examinez les opérations asynchrones suivantes:

  • Définir des règles de sécurité, par exemple avec initializeTestEnvironment
  • Lire et écrire des données, avec par exemple db.collection("users").doc("alice").get()
  • Les assertions opérationnelles, y compris assertSucceeds et assertFails.

Les tests ne réussissent que la première fois que vous chargez l'émulateur

L'émulateur est avec état. Il stocke toutes les données qui y sont écrites en mémoire. Ainsi, toutes les données sont perdues à chaque arrêt de l'émulateur. Si vous exécutez plusieurs tests par rapport au même ID de projet, chaque test peut produire des données susceptibles d'influencer les tests suivants. Vous pouvez utiliser l'une des méthodes suivantes pour contourner ce comportement:

  • Utilisez des ID de projet uniques pour chaque test. Notez que si vous choisissez de le faire, vous devrez appeler initializeTestEnvironment dans le cadre de chaque test. Les règles ne sont chargées automatiquement que pour l'ID de projet par défaut.
  • Restructurez vos tests de sorte qu'ils n'interagissent pas avec les données écrites précédemment (par exemple, utilisez une collection différente pour chaque test).
  • Supprimez toutes les données écrites lors d'un test.

La configuration des tests est très compliquée.

Lors de la configuration du test, vous souhaiterez peut-être modifier les données conformément aux règles de sécurité de Firestore. Si vos règles rendent la configuration de test complexe, essayez d'utiliser RulesTestEnvironment.withSecurityRulesDisabled lors des étapes de configuration. Les lectures et les écritures ne déclencheront donc pas d'erreurs PERMISSION_DENIED.

Ensuite, votre test peut effectuer des opérations en tant qu'utilisateur authentifié ou non authentifié à l'aide, respectivement, de RulesTestEnvironment.authenticatedContext et de unauthenticatedContext. Cela vous permet de vérifier que vos règles de sécurité Firestore autorisent ou refusent correctement les différents cas.