Test delle regole di sicurezza
Durante la creazione dell'app, potresti voler bloccare l'accesso al tuo database Firestore. Tuttavia, prima del lancio, ti serviranno più sfumature per le regole di sicurezza di Firestore. Con l'emulatore Firestore, oltre a prototipare e testare le funzionalità e il comportamento generali dell'app, puoi scrivere test delle unità che verificano il comportamento delle regole di sicurezza Firestore.
Guida rapida
Per alcuni scenari di test di base con regole semplici, prova l'esempio di guida rapida.
Informazioni sulle regole di sicurezza di Firestore
Implementa le regole di sicurezza di Firebase Authentication e Firestore per l'autenticazione, l'autorizzazione e la convalida dei dati serverless quando utilizzi le librerie client per dispositivi mobili e web.
Le regole di sicurezza di Firestore includono due parti:
- Un'istruzione
match
che identifica i documenti nel database. - Un'espressione
allow
che controlla l'accesso a quei documenti.
Firebase Authentication verifica le credenziali degli utenti e fornisce le basi per sistemi di accesso basati sugli utenti e sui ruoli.
Ogni richiesta di database da una libreria client web/mobile di Firestore viene valutata in base alle regole di sicurezza prima di leggere o scrivere dati. Se le regole negano l'accesso a uno dei percorsi del documento specificati, l'intera richiesta non riesce.
Scopri di più sulle regole di sicurezza Firestore nella guida introduttiva alle regole di sicurezza Firestore.
Installa l'emulatore
Per installare l'emulatore Firestore, utilizza l'interfaccia a riga di comando di Firebase ed esegui il comando seguente:
firebase setup:emulators:firestore
Esegui l'emulatore
Inizia inizializzando un progetto Firebase nella directory di lavoro. Questo è un primo passaggio comune quando utilizzi l'interfaccia a riga di comando di Firebase.
firebase init
Avvia l'emulatore utilizzando il comando seguente. L'emulatore verrà eseguito finché non termina la procedura:
firebase emulators:start --only firestore
In molti casi è necessario avviare l'emulatore, eseguire una suite di test e quindi arrestare l'emulatore dopo l'esecuzione dei test. Puoi farlo facilmente usando il comando emulators:exec
:
firebase emulators:exec --only firestore "./my-test-script.sh"
All'avvio, l'emulatore proverà a essere eseguito su una porta predefinita (8080). Puoi cambiare la porta dell'emulatore modificando la sezione "emulators"
del file firebase.json
:
{ // ... "emulators": { "firestore": { "port": "YOUR_PORT" } } }
Prima di eseguire l'emulatore
Prima di iniziare a utilizzare l'emulatore, tieni presente quanto segue:
- L'emulatore caricherà inizialmente le regole specificate nel campo
firestore.rules
del filefirebase.json
. Prevede il nome di un file locale contenente le regole di sicurezza Firestore e applica queste regole a tutti i progetti. Se non fornisci il percorso del file locale o utilizzi il metodoloadFirestoreRules
come descritto di seguito, l'emulatore considera tutte le regole aperte come regole. - Anche se la maggior parte degli SDK Firebase funziona direttamente con gli emulatori, solo la libreria
@firebase/rules-unit-testing
supporta il simulamento diauth
nelle regole di sicurezza, il che semplifica notevolmente i test delle unità. Inoltre, la libreria supporta alcune funzionalità specifiche degli emulatori come la cancellazione di tutti i dati, come elencato di seguito. - Gli emulatori accettano anche token di autenticazione Firebase di produzione forniti tramite gli SDK client e valutano le regole di conseguenza, il che consente di connettere l'applicazione direttamente agli emulatori nei test di integrazione e manuali.
Esecuzione dei test delle unità locali
Eseguire test delle unità locali con l'SDK JavaScript v9
Firebase distribuisce una libreria di test delle unità delle regole di sicurezza sia con l'SDK JavaScript versione 9 che con l'SDK versione 8. Le API delle librerie sono significativamente diverse. Consigliamo la libreria di test v9, che è più semplificata e richiede meno configurazione per la connessione agli emulatori ed evita così l'uso involontario delle risorse di produzione. Per la compatibilità con le versioni precedenti, continuiamo a rendere disponibile la libreria di test v8.
- Metodi di test e funzioni di utilità comuni nell'SDK v9
- Metodi di test specifici dell'emulatore nell'SDK v9
Utilizza il modulo @firebase/rules-unit-testing
per interagire con l'emulatore eseguito localmente. Se vengono visualizzati errori di timeout o ECONNREFUSED
, assicurati
che l'emulatore sia effettivamente in esecuzione.
Consigliamo vivamente di utilizzare una versione recente di Node.js in modo da poter utilizzare la notazione async/await
. Quasi tutti i comportamenti che potresti voler testare riguardano le funzioni asincrone e il modulo di test è progettato per funzionare con codice basato su Promise.
La libreria di test dell'unità delle regole v9 è sempre a conoscenza degli emulatori e non tocca mai le risorse di produzione.
Puoi importare la libreria utilizzando istruzioni di importazione modulare v9. Ad esempio:
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.
Una volta importato, l'implementazione dei test delle unità prevede:
- Creazione e configurazione di un
RulesTestEnvironment
con una chiamata ainitializeTestEnvironment
. - Impostare dati di test senza attivare regole, utilizzando un metodo
che ti consente di ignorarli temporaneamente,
RulesTestEnvironment.withSecurityRulesDisabled
. - Configurazione di suite di test e hook prima/dopo con chiamate per pulire i dati e l'ambiente di test, ad esempio
RulesTestEnvironment.cleanup()
oRulesTestEnvironment.clearFirestore()
. - Implementare scenari di test che imitano gli stati di autenticazione utilizzando
RulesTestEnvironment.authenticatedContext
eRulesTestEnvironment.unauthenticatedContext
.
Metodi comuni e funzioni di utilità
Consulta anche i metodi di test specifici dell'emulatore nell'SDK v9.
initializeTestEnvironment() => RulesTestEnvironment
Questa funzione inizializza un ambiente di test per il test delle unità delle regole. Chiama prima questa funzione per la configurazione del test. Per l'esecuzione corretta è necessario che gli emulatori siano in esecuzione.
La funzione accetta un oggetto facoltativo che definisce un TestEnvironmentConfig
, che può essere costituito da un ID progetto e impostazioni di configurazione dell'emulatore.
let testEnv = await initializeTestEnvironment({ projectId: "demo-project-1234", firestore: { rules: fs.readFileSync("firestore.rules", "utf8"), }, });
RulesTestEnvironment.authenticatedContext({ user_id: string, tokenOptions?: TokenOptions }) => RulesTestContext
Questo metodo crea un RulesTestContext
, che si comporta come un utente autenticato di autenticazione. Alle richieste create tramite il contesto restituito verrà allegato un token di autenticazione fittizio. Facoltativamente, trasmetti un oggetto che definisce richieste o sostituzioni personalizzate per i payload dei token di autenticazione.
Utilizza l'oggetto di contesto del test restituito nei test per accedere a eventuali istanze dell'emulatore configurate, incluse quelle configurate con 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
Questo metodo crea un RulesTestContext
, che si comporta come un client che non è collegato tramite autenticazione. Alle richieste create tramite il contesto restituito non verranno associati i token Firebase Auth.
Utilizza l'oggetto di contesto del test restituito nei test per accedere a eventuali istanze dell'emulatore configurate, incluse quelle configurate con 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()
Eseguire una funzione di configurazione di test con un contesto che funziona come se le regole di sicurezza fossero disattivate.
Questo metodo utilizza una funzione di callback, che accetta il contesto in base alle regole di sicurezza superando il contesto e restituisce una promessa. Il contesto verrà eliminato una volta risolta / rifiutata la promessa.
RulesTestEnvironment.cleanup()
Questo metodo distrugge tutti i RulesTestContexts
creati nell'ambiente di test e ripulisce le risorse sottostanti, consentendo un'uscita pulita.
Questo metodo non modifica in alcun modo lo stato degli emulatori. Per reimpostare i dati tra un test e l'altro, utilizza il metodo chiaro e specifico per l'emulatore di applicazione.
assertSucceeds(pr: Promise<any>)) => Promise<any>
Questa è una funzione di utilità dello scenario di test.
La funzione afferma che la promessa specificata di un'operazione dell'emulatore verrà risolta senza violazioni delle regole di sicurezza.
await assertSucceeds(setDoc(alice.firestore(), '/users/alice'), { ... });
assertFails(pr: Promise<any>)) => Promise<any>
Questa è una funzione di utilità dello scenario di test.
La funzione afferma che la promessa fornita che esegue un'operazione dell'emulatore verrà rifiutata con una violazione delle regole di sicurezza.
await assertFails(setDoc(alice.firestore(), '/users/bob'), { ... });
Metodi specifici dell'emulatore
Consulta anche l'articolo relativo ai metodi di test e alle funzioni di utilità comuni nell'SDK v9.
RulesTestEnvironment.clearFirestore() => Promise<void>
Questo metodo cancella i dati nel database Firestore che appartiene al projectId
configurato per l'emulatore Firestore.
RulesTestContext.firestore(settings?: Firestore.FirestoreSettings) => Firestore;
Questo metodo riceve un'istanza Firestore per questo contesto di test. L'istanza dell'SDK client Firebase JS restituita può essere utilizzata con le API SDK client (v9 modulare o v9 compat).
Visualizza le valutazioni delle regole
L'emulatore Firestore ti consente di visualizzare le richieste dei client nell'interfaccia utente di Emulator Suite, incluso il tracciamento della valutazione per le regole di sicurezza Firebase.
Apri la scheda Richieste Fire > per visualizzare la sequenza di valutazione dettagliata per ogni richiesta.
Genera report di prova
Dopo aver eseguito una suite di test, puoi accedere ai report sulla copertura dei test che mostrano come è stata valutata ciascuna delle tue regole di sicurezza.
Per ottenere i report, esegui una query su un endpoint esposto sull'emulatore mentre è in esecuzione. Per una versione ottimizzata per il browser, utilizza il seguente URL:
http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage.html
Suddivide le regole in espressioni e sottoespressioni che puoi spostare con il mouse per ulteriori informazioni, tra cui il numero di valutazioni e valori restituiti. Per la versione JSON non elaborata di questi dati, includi il seguente URL nella tua query:
http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage
Differenze tra l'emulatore e la produzione
- Non è necessario creare esplicitamente un progetto Firestore. L'emulatore crea automaticamente le eventuali istanze a cui si accede.
- L'emulatore Firestore non funziona con il normale flusso di Firebase Authentication.
Nell'SDK Firebase Test abbiamo invece fornito il metodo
initializeTestApp()
nella libreriarules-unit-testing
, che utilizza un campoauth
. L'handle di Firebase creato con questo metodo funziona come se fosse stato autenticato correttamente, indipendentemente dall'entità che fornisci. Se passi anull
, questo si comporterà come un utente non autenticato (ad esempio, le regoleauth != null
non andranno a buon fine).
Risolvere i problemi noti
Durante l'utilizzo dell'emulatore Firestore, potrebbero verificarsi i seguenti problemi noti. Segui le indicazioni riportate di seguito per risolvere i problemi relativi a comportamenti irregolari. Queste note sono scritte tenendo a mente la libreria di test dell'unità di regole di sicurezza, ma gli approcci generali sono applicabili a qualsiasi SDK Firebase.
Il comportamento dei test non è coerente
Se i test superano occasionalmente i test e non hanno esito positivo, anche senza modifiche ai test stessi, potresti dover verificare che siano in sequenza appropriata.
Poiché la maggior parte delle interazioni con l'emulatore è asincrona, verifica che tutto il codice asincrono sia inserito in sequenza corretta. Puoi correggere la sequenza concatenando le promesse o utilizzando la notazione await
liberamente.
In particolare, esamina le seguenti operazioni asincrone:
- Impostazione delle regole di sicurezza, ad esempio
initializeTestEnvironment
. - Leggere e scrivere dati, ad esempio
db.collection("users").doc("alice").get()
. - Affermazioni operative, tra cui
assertSucceeds
eassertFails
.
I test superano il primo caricamento dell'emulatore
L'emulatore è stateful. Archivia tutti i dati scritti in memoria, quindi tutti i dati vengono persi ogni volta che l'emulatore si arresta. Se stai eseguendo più test sullo stesso ID progetto, ogni test può produrre dati che potrebbero influire sui test successivi. Puoi utilizzare uno dei seguenti metodi per ignorare questo comportamento:
- Utilizza ID progetto univoci per ogni test. Tieni presente che, se scegli di farlo, dovrai chiamare
initializeTestEnvironment
come parte di ogni test; le regole vengono caricate automaticamente solo per l'ID progetto predefinito. - Riorganizza i test in modo che non interagiscano con i dati scritti in precedenza (ad esempio, utilizza una raccolta diversa per ogni test).
- Elimina tutti i dati scritti durante un test.
La configurazione dei test è molto complicata
Quando configuri il test, puoi modificare i dati in un modo che le Regole di sicurezza di Firestore non consentano effettivamente. Se le regole rendono la configurazione di test complessa, prova a utilizzare RulesTestEnvironment.withSecurityRulesDisabled
nei passaggi di configurazione, per cui le operazioni di lettura e scrittura non attiveranno errori di PERMISSION_DENIED
.
Dopodiché il test può eseguire le operazioni come utente autenticato o non autenticato utilizzando RulesTestEnvironment.authenticatedContext
e unauthenticatedContext
. Ciò consente di verificare che le regole di sicurezza Firestore consentano / negano casi diversi correttamente.