Testar regras de segurança
Durante o desenvolvimento do seu aplicativo, talvez seja interessante bloquear o acesso ao banco de dados do Firestore. No entanto, antes do lançamento, será necessário adicionar mais detalhes às regras de segurança do Firestore. Com o emulador do Firestore, além de prototipar e testar os recursos e comportamentos gerais do app, é possível criar testes de unidade que verificam o comportamento das regras de segurança do Firestore.
Guia de início rápido
Para alguns casos de teste básicos com regras simples, utilize o exemplo do guia de início rápido.
Como entender as regras de segurança do Firestore
Implemente o Firebase Authentication e as regras de segurança do Firestore para autenticação, autorização e validação de dados sem servidor ao usar as bibliotecas de cliente da Web e para dispositivos móveis.
As regras de segurança do Firestore incluem duas partes:
- Uma instrução
match
que identifica documentos no banco de dados. - Uma expressão
allow
que controla o acesso a esses documentos.
O Firebase Authentication verifica as credenciais dos usuários e fornece as bases para sistemas de acesso de acordo com usuários e papéis.
Toda solicitação de uma biblioteca de cliente da Web/para dispositivos móveis do Firestore para o banco de dados é avaliada em relação às regras de segurança antes de ler ou gravar dados. Se as regras negarem o acesso a qualquer um dos caminhos de documento especificados, a solicitação falhará como um todo.
Saiba mais sobre as regras de segurança do Firestore em Primeiros passos com as regras de segurança do Firestore.
Instalar o emulador
Para instalar o emulador do Firestore, use a Firebase CLI e execute o comando abaixo:
firebase setup:emulators:firestore
Executar o emulador
Comece inicializando um projeto do Firebase no seu diretório de trabalho. Essa é uma primeira etapa comum ao usar a CLI do Firebase.
firebase init
Inicie o emulador usando o comando a seguir. O emulador será executado até você encerrar o processo:
firebase emulators:start --only firestore
Em muitos casos, você quer iniciar o emulador, executar um conjunto de testes e, em seguida,
desligar o emulador. É possível fazer isso facilmente usando o
comando emulators:exec
:
firebase emulators:exec --only firestore "./my-test-script.sh"
Quando iniciado, o emulador tentará ser executado em uma porta padrão (8080). Para alterar a porta do emulador, modifique a seção "emulators"
do arquivo firebase.json
:
{ // ... "emulators": { "firestore": { "port": "YOUR_PORT" } } }
Antes de executar o emulador
Considere as seguintes informações antes de começar a usar o emulador:
- Inicialmente, o emulador carregará as regras especificadas no campo
firestore.rules
do seu arquivofirebase.json
. Ele espera receber o nome de um arquivo local que contém suas regras de segurança do Firestore e aplica essas regras a todos os projetos. Se você não fornecer o caminho do arquivo local ou usar o métodoloadFirestoreRules
, conforme descrito abaixo, o emulador tratará todos os projetos como tendo regras abertas. - Enquanto
a maioria dos SDKs do Firebase
trabalha com emuladores diretamente, somente a biblioteca
@firebase/rules-unit-testing
é compatível com a simulação deauth
nas regras de segurança, o que facilita muito os testes de unidade. Além disso, a biblioteca é compatível com alguns recursos específicos de emulador, como limpar todos os dados, conforme listado abaixo. - Os emuladores também vão aceitar tokens de produção do Firebase Authentication fornecidos pelos SDKs do cliente e avaliarão as regras de acordo, o que permite conectar o aplicativo diretamente aos emuladores em testes manuais e de integração.
Executar testes de unidade locais
Executar testes de unidade locais com o SDK para JavaScript v9
O Firebase distribui uma biblioteca de testes de unidade de regras de segurança com o SDK para JavaScript versão 9 e 8. As APIs da biblioteca são significativamente diferentes. Recomendamos a biblioteca de testes v9, que é mais simples e requer menos configuração para se conectar a emuladores e, portanto, evita o uso acidental de recursos de produção. Para oferecer compatibilidade com versões anteriores, continuamos a disponibilizar a biblioteca de testes v8.
- Métodos de teste comuns e funções utilitárias no SDK v9
- Métodos de teste específicos do emulador no SDK v9
Use o módulo @firebase/rules-unit-testing
para interagir com o emulador
executado localmente. Se você alcançar o tempo limite ou receber erros ECONNREFUSED
, verifique
se o emulador está sendo executado.
Recomendamos o uso de uma versão recente do Node.js para que você possa usar a
notação async/await
. Quase todo o comportamento que pode ser testado
envolve funções assíncronas, e o módulo de teste é projetado para funcionar
com código baseado em promessas.
A biblioteca de teste de unidade de regras v9 está sempre ciente dos emuladores e nunca interfere nos recursos de produção.
A biblioteca é importada usando instruções de importação modular v9. Exemplo:
import {
assertFails,
assertSucceeds,
initializeTestEnvironment
} 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.
Depois de importados, a implementação de testes de unidade envolve:
- a criação e a configuração de um
RulesTestEnvironment
com uma chamada parainitializeTestEnvironment
; - a configuração de dados de teste sem acionar regras, usando um método de
conveniência que permite ignorá-las temporariamente,
RulesTestEnvironment.withSecurityRulesDisabled
; - a configuração do conjunto de testes e os hooks por teste antes/depois com as chamadas para
limpar os dados e o ambiente de teste, como
RulesTestEnvironment.cleanup()
ouRulesTestEnvironment.clearFirestore()
; - a implementação de casos de teste que imitam estados de autenticação usando
RulesTestEnvironment.authenticatedContext
eRulesTestEnvironment.unauthenticatedContext
.
Métodos comuns e funções utilitárias
Veja também métodos de testes específicos do emulador no SDK v9.
initializeTestEnvironment() => RulesTestEnvironment
Essa função inicializa um ambiente de teste para testes de unidade de regras. Chame essa função primeiro para configurar o teste. A execução bem-sucedida requer que os emuladores estejam em execução.
A função aceita um objeto opcional que define um TestEnvironmentConfig
,
que pode consistir em um ID do projeto e em configurações do emulador.
let testEnv = await initializeTestEnvironment({ projectId: "demo-project-1234", firestore: { rules: fs.readFileSync("firestore.rules", "utf8"), }, });
RulesTestEnvironment.authenticatedContext({ user_id: string, tokenOptions?: TokenOptions }) => RulesTestContext
Esse método cria um RulesTestContext
, que se comporta como um usuário autenticado
do Authentication. As solicitações criadas pelo contexto retornado terão um token
simulado do Authentication anexados. Opcionalmente, transmita um objeto que define declarações personalizadas ou
substituições para payloads de tokens do Authentication.
Use o objeto de contexto de teste retornado nos testes para acessar qualquer instância
configurada do emulador, incluindo as configuradas com 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
Esse método cria um RulesTestContext
, que se comporta como um cliente não
conectado pelo Authentication. As solicitações criadas pelo contexto retornado não
terão tokens do Firebase Auth anexados.
Use o objeto de contexto de teste retornado nos testes para acessar qualquer instância
configurada do emulador, incluindo as configuradas com 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()
Execute uma função de configuração de teste com um contexto que se comporte como se as regras de segurança estivessem desativadas.
Esse método usa uma função de callback com um contexto que ignora as regras de segurança e retorna uma promessa. O contexto será destruído quando a promessa for resolvida ou rejeitada.
RulesTestEnvironment.cleanup()
Esse método destrói todos os RulesTestContexts
criados no ambiente de teste e
limpa os recursos restantes, permitindo uma saída limpa.
Esse método não altera o estado dos emuladores. Para redefinir dados entre testes, use o método do aplicativo de dados limpos específico para emulador.
assertSucceeds(pr: Promise<any>)) => Promise<any>
Esta é uma função utilitária do caso de teste.
A função declara que a promessa fornecida que envolve uma operação de emulador será resolvida sem violações das regras de segurança.
await assertSucceeds(setDoc(alice.firestore(), '/users/alice'), { ... });
assertFails(pr: Promise<any>)) => Promise<any>
Esta é uma função utilitária do caso de teste.
A função declara que a promessa fornecida que envolve uma operação de emulador será rejeitada com uma violação das regras de segurança.
await assertFails(setDoc(alice.firestore(), '/users/bob'), { ... });
Métodos específicos para emulador
Veja também métodos de testes comuns e funções utilitárias no SDK v9.
RulesTestEnvironment.clearFirestore() => Promise<void>
Esse método limpa dados no banco de dados do Firestore que pertence ao
projectId
configurado para o emulador do Firestore.
RulesTestContext.firestore(settings?: Firestore.FirestoreSettings) => Firestore;
Esse método recebe uma instância do Firestore para este contexto de teste. A instância retornada do SDK do cliente do Firebase para JS pode ser usada com as APIs do SDK do cliente (v9 modular ou v9 compatível).
Visualizar avaliações de regras
O emulador do Firestore permite visualizar solicitações de cliente na IU do Pacote do emulador, incluindo o rastreamento de avaliação das regras de segurança do Firebase.
Abra a guia Firestore > Solicitações para visualizar a sequência de avaliação detalhada para cada solicitação.
Gerar relatórios de teste
Depois de executar um conjunto de testes, é possível acessar relatórios de cobertura de teste que mostram como cada uma das regras de segurança foi avaliada.
Para acessar os relatórios, consulte um endpoint exposto no emulador enquanto ele está em execução. Para uma versão otimizada para navegadores, use o seguinte URL:
http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage.html
Isso divide suas regras em expressões e subexpressões em que é possível passar o mouse para encontrar mais informações, incluindo o número de execuções e os valores retornados. Para a versão em JSON bruta desses dados, inclua o seguinte URL em sua consulta:
http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage
Diferenças entre o emulador e a produção
- Não é necessário criar um projeto do Firestore de maneira explícita. O emulador criará automaticamente qualquer instância que for acessada.
- O emulador do Firestore não funciona com o fluxo normal do Firebase Authentication.
Em vez disso, no SDK de teste do Firebase, fornecemos o método
initializeTestApp()
na bibliotecarules-unit-testing
, que usa um campoauth
. O gerenciador do Firebase, criado com esse método, se comportará como se tivesse sido autenticado da mesma forma que qualquer entidade fornecida por você. Casonull
seja transmitido, ele se comportará como um usuário não autenticado. Por exemplo, as regrasauth != null
falharão.
Resolver problemas conhecidos
Ao usar o emulador do Firestore, é possível que você encontre os problemas conhecidos a seguir. Siga as orientações abaixo para resolver qualquer comportamento irregular que ocorrer. Essas observações foram escritas pensando na biblioteca de teste de unidade de regras de segurança, mas as abordagens gerais são aplicáveis a qualquer SDK do Firebase.
O comportamento do teste é inconsistente
Caso os testes estejam sendo aprovados e apresentando falha em algumas ocasiões, mesmo sem nenhuma alteração neles, pode ser necessário verificar se eles estão devidamente sequenciados.
A maioria das interações com o emulador é assíncrona. Dessa forma, verifique novamente se todo o código assíncrono está sequenciado adequadamente. Para corrigir o sequenciamento, encadeie as promessas ou use a notação await
livremente.
Especificamente, analise as seguintes operações assíncronas:
- Definir regras de segurança com, por exemplo,
initializeTestEnvironment
. - Ler e gravar dados com, por exemplo,
db.collection("users").doc("alice").get()
. - Declarações operacionais, incluindo
assertSucceeds
eassertFails
.
Os testes são aprovados apenas na primeira vez que você carrega o emulador
O emulador com estado armazena todos os dados gravados na memória. Dessa forma, todos eles são perdidos sempre que o emulador é desligado. Se você estiver executando vários testes com o mesmo código do projeto, cada um deles poderá produzir dados que podem influenciar os testes subsequentes. É possível usar qualquer um dos seguintes métodos para ignorar esse comportamento:
- Use IDs de projetos exclusivos para cada teste. Se você optar por fazer isso,
será necessário chamar
initializeTestEnvironment
como parte de cada teste. As regras são carregadas automaticamente apenas para o ID do projeto padrão. - Reestruture seus testes para que eles não interajam com dados gravados anteriormente (por exemplo, use uma coleção diferente para cada teste).
- Exclua todos os dados gravados durante um teste.
A configuração do teste é muito complicada
Ao configurar seu teste, talvez você queira modificar os dados de forma que as
regras de segurança do Cloud Firestore não permitam. Se as regras estiverem tornando a configuração de teste
complexa, tente usar RulesTestEnvironment.withSecurityRulesDisabled
nas etapas de
configuração para que as leituras e gravações não acionem erros PERMISSION_DENIED
.
Depois disso, o teste pode executar operações como um usuário autenticado ou não autenticado
usando RulesTestEnvironment.authenticatedContext
e unauthenticatedContext
,
respectivamente. Isso permite que você verifique se as regras de segurança do Firestore permitem ou negam
casos diferentes corretamente.