Ir para o console

Teste suas regras de segurança do Cloud Firestore

Durante o desenvolvimento do seu app, talvez seja interessante bloquear o acesso ao banco de dados do Cloud Firestore. No entanto, antes do lançamento, será necessário adicionar mais detalhes às regras de segurança do Cloud Firestore. Com o emulador do Cloud Firestore, você pode criar testes de unidade que verificam o comportamento das suas regras de segurança do Cloud Firestore.

Guia de início rápido

Para alguns casos de teste básicos com regras simples, utilize o guia de início rápido do JavaScript ou o guia de início rápido do TypeScript (ambos em inglês).

Compreender as regras de segurança do Cloud Firestore

Implemente o Firebase Authentication e as regras de segurança do Cloud 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 Cloud Firestore incluem dois elementos:

  1. uma instrução match que identifica documentos no seu banco de dados
  2. 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.

Todas as solicitações de bibliotecas de cliente da Web ou para dispositivos móveis do Cloud Firestore ao seu banco de dados são avaliadas em relação às regras de segurança antes da leitura ou gravação de 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 Cloud Firestore em Primeiros passos com as regras de segurança do Cloud Firestore.

Instalar o emulador

Para instalar o emulador do Cloud Firestore, use a Firebase CLI e siga as etapas abaixo.

  1. Instale o emulador do Cloud Firestore:

     firebase setup:emulators:firestore
     

  2. Inicie o emulador usando o comando a seguir. O emulador é executado durante todos os seus testes.

     firebase serve --only firestore
     

Antes de executar o emulador

Considere as seguintes informações antes de começar a usar o emulador:

  • O único SDK compatível atualmente com o emulador é o SDK para Node.js. Fornecemos o módulo @firebase/testing para facilitar a interação com o emulador.
  • O emulador é compatível com uma CLI --rules opcional. Ele espera receber o nome de um arquivo local que contém as regras de segurança do Cloud Firestore e aplica essas regras a todos os projetos. Se você não fornecer o caminho do arquivo local ou usar o método loadFirestoreRules, conforme descrito abaixo, o emulador tratará todos os projetos como tendo regras abertas.

Executar testes locais

Use o módulo @firebase/testing para interagir com o emulador executado localmente. Se você tiver tempos limites ou erros ECONNREFUSED, verifique novamente se o emulador está em execução.

É altamente recomendável o uso de uma versão recente do Node.js para utilizar a notação async/await. Quase todos os comportamentos que podem ser testados envolvem funções assíncronas, e o módulo de teste é projetado para funcionar com código baseado em promessas.

O módulo expõe os seguintes métodos:

  • initializeTestApp({ projectId: <name>, auth: <auth> }) => FirebaseApp

    Esse método retorna um aplicativo do Firebase inicializado correspondente ao código do projeto e à variável de autenticação especificada nas opções. Use esse método para criar um aplicativo autenticado como um usuário específico a ser utilizado nos testes.

     firebase.initializeTestApp({
       projectId: "my-test-project",
       auth: { uid: "alice", email: "alice@example.com" }
     });
    
  • initializeAdminApp({ projectId: <name> }) => FirebaseApp

    Esse método retorna um aplicativo de administrador do Firebase inicializado. Esse aplicativo ignora as regras de segurança ao executar leituras e gravações. Use esse método para criar um aplicativo autenticado como administrador e configurar o estado para testes.

    firebase.initializeAdminApp({ projectId: "my-test-project" });
    
  • apps() => [FirebaseApp] Esse método retorna todos os app de teste e administração inicializados no momento. Ele pode ser usado para limpar aplicativos durante e depois dos testes.

     Promise.all(firebase.apps().map(app => app.delete()))
    
  • loadFirestoreRules({ projectId: <name>, rules: <rules> }) => Promise

    Esse método envia regras para um banco de dados em execução localmente. Ele utiliza um objeto que especifica as regras como uma string. Use esse método para definir as regras do seu banco de dados.

     firebase.loadFirestoreRules({
       projectId: "my-test-project",
       rules: fs.readFileSync("/path/to/firestore.rules", "utf8")
     });
    
  • assertFails(pr: Promise) => Promise

    Esse método retorna uma promessa que será recusada se a entrada for concluída e que será concluída se a entrada for recusada. Ele deve ser usado para informar se ocorreu uma falha na leitura ou na gravação no banco de dados.

    firebase.assertFails(app.firestore().collection("private").doc("super-secret-document").get());
    
  • assertSucceeds(pr: Promise) => Promise

    Esse método retorna uma promessa que será concluída se a entrada for concluída e será recusada se a entrada for recusada. Ele deve ser usado para informar se uma leitura ou gravação no banco de dados foi concluída.

    firebase.assertSucceeds(app.firestore().collection("public").doc("test-document").get());
    
  • clearFirestoreData({ projectId: <name> }) => Promise

    Esse método limpa todos os dados associados a um projeto específico na instância do Firestore que estão em execução localmente. Use o método para limpar os dados após os testes.

    firebase.clearFirestoreData({
     projectId: "my-test-project"
    });
    

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 suas regras de segurança foi avaliada.

Para acessar os relatórios, consulte um ponto de extremidade 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 você pode passar o mouse para ver 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

  1. Você não precisa criar um projeto do Cloud Firestore de maneira explícita. O emulador criará automaticamente qualquer instância que for acessada.
  2. O emulador do Cloud Firestore não interage ativamente com outros produtos do Firebase. Além disso, é importante ressaltar que o fluxo normal do Firebase Authentication não funcionará. Em vez disso, fornecemos no módulo de teste o método initializeTestApp(), que recebe um campo auth. O gerenciador do Firebase criado a partir desse método se comportará como se tivesse sido autenticado da mesma forma que qualquer entidade fornecida por você. Caso null seja transmitido, ele se comportará como um usuário não autenticado. Por exemplo, as regras auth != null apresentarão falha.
  3. O emulador do Cloud Firestore mantém dados. Isso pode afetar seus resultados. Para executar testes de maneira independente, atribua um código do projeto diferente para cada teste individual. Quando você chamar firebase.initializeAdminApp ou firebase.initializeTestApp, inclua um código, carimbo de data e hora ou número inteiro aleatório exclusivo ao projectID. Também é possível resolver as condições de corrida ao aguardar a resolução das promessas por meio do método assíncrono ou ao aguardar palavras-chave no JavaScript.

Resolver problemas conhecidos

Ao usar o emulador do Cloud Firestore, você pode se deparar com os problemas conhecidos a seguir. Siga as orientações abaixo para resolver qualquer comportamento irregular que ocorrer.

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á apropriadamente sequenciado. É possível solucionar o sequenciamento ao encadear as promessas ou ao usar a notação de await sem restrições.

Especificamente, analise as seguintes operações assíncronas: - Como configurar regras, com, por exemplo, firebase.loadFirestoreRules. - Como ler e gravar dados, com, por exemplo, db.collection("users").doc("alice").get(). - Asserções operacionais, incluindo firebase.assertSucceeds e firebase.assertFails.

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 códigos de projeto exclusivos para cada teste.
  • 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

Pode ser necessário testar cenários que suas regras de Cloud Firestore não permitem. Por exemplo, testar se usuários não autenticados podem editar dados é uma tarefa difícil, porque não é possível editar dados como um usuário não autenticado.

Caso as regras estejam prejudicando a configuração do teste, utilize um cliente autorizado pelo administrador para ignorar as regras. É possível fazer isso com firebase.initializeAdminApp. As leituras e gravações de clientes autorizados pelo administrador ignoram as regras e não acionam erros de PERMISSION_DENIED.