Catch up on everything announced at Firebase Summit, and learn how Firebase can help you accelerate app development and run your app with confidence. Learn More

Teste suas regras de segurança do Cloud Firestore

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

À medida que você cria seu aplicativo, convém bloquear o acesso ao banco de dados do Cloud Firestore. No entanto, antes de iniciar, você precisará de regras de segurança do Cloud Firestore mais diferenciadas. Com o emulador do Cloud Firestore, além de prototipar e testar os recursos gerais e o comportamento do seu aplicativo , você pode escrever testes de unidade que verificam o comportamento das regras de segurança do Cloud Firestore.

Começo rápido

Para alguns casos de teste básicos com regras simples, experimente o exemplo de início rápido .

Entenda as regras de segurança do Cloud Firestore

Implemente as regras de segurança do Firebase Authentication e do Cloud Firestore para autenticação sem servidor, autorização e validação de dados ao usar as bibliotecas de clientes móveis e da Web.

As regras de segurança do Cloud Firestore incluem duas partes:

  1. Uma declaração de match que identifica documentos em seu banco de dados.
  2. Uma expressão de allow que controla o acesso a esses documentos.

O Firebase Authentication verifica as credenciais dos usuários e fornece a base para sistemas de acesso baseados em usuário e função.

Cada solicitação de banco de dados de uma biblioteca de cliente móvel/da Web do Cloud Firestore é avaliada em relação às suas regras de segurança antes de ler ou gravar qualquer dado. Se as regras negarem acesso a qualquer um dos caminhos de documento especificados, toda a solicitação falhará.

Saiba mais sobre as regras de segurança do Cloud Firestore em Introdução às regras de segurança do Cloud Firestore .

Instale o emulador

Para instalar o emulador do Cloud Firestore, use a Firebase CLI e execute o comando abaixo:

firebase setup:emulators:firestore

Execute o emulador

Comece inicializando um projeto do Firebase em seu diretório de trabalho. Essa é uma primeira etapa comum ao usar a Firebase CLI .

firebase init

Inicie o emulador usando o comando a seguir. O emulador será executado até você matar o processo:

firebase emulators:start --only firestore

Em muitos casos, você deseja iniciar o emulador, executar um conjunto de testes e desligar o emulador após a execução dos testes. Você pode 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). Você pode alterar a porta do emulador modificando a seção "emulators" do seu arquivo firebase.json :

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

Antes de executar o emulador

Antes de começar a usar o emulador, lembre-se do seguinte:

  • O emulador carregará inicialmente as regras especificadas no campo firestore.rules do seu arquivo firebase.json . Ele espera o nome de um arquivo local contendo suas 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.
  • Embora a maioria dos SDKs do Firebase funcione diretamente com os emuladores, apenas a biblioteca @firebase/rules-unit-testing é compatível com a auth nas regras de segurança, facilitando muito os testes de unidade. Além disso, a biblioteca oferece suporte a alguns recursos específicos do emulador, como limpar todos os dados, conforme listado abaixo.
  • Os emuladores também aceitarão tokens de autenticação do Firebase de produção fornecidos por meio de SDKs de cliente e avaliarão as regras de acordo, o que permite conectar seu aplicativo diretamente aos emuladores em testes manuais e de integração.

Executar testes de unidade locais

Execute testes de unidade local com o SDK do JavaScript v9

O Firebase distribui uma biblioteca de teste de unidade de regras de segurança com o SDK JavaScript da versão 9 e o SDK da versão 8. As APIs da biblioteca são significativamente diferentes. Recomendamos a biblioteca de testes v9, que é mais simplificada e requer menos configuração para se conectar a emuladores e, assim, evitar com segurança o uso acidental de recursos de produção. Para compatibilidade com versões anteriores, continuamos a disponibilizar a biblioteca de teste v8 .

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

É altamente recomendável usar uma versão recente do Node.js para que você possa usar a notação async/await . Quase todo o comportamento que você pode querer testar envolve funções assíncronas, e o módulo de teste foi projetado para funcionar com código baseado em Promise.

A biblioteca v9 Rules Unit Testing está sempre ciente dos emuladores e nunca afeta seus recursos de produção.

Você importa a biblioteca usando instruções de importação modulares v9. Por exemplo:

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.

Uma vez importado, a implementação de testes de unidade envolve:

  • Criando e configurando um RulesTestEnvironment com uma chamada para initializeTestEnvironment .
  • Configurando dados de teste sem acionar Regras, usando um método conveniente que permite ignorá-los temporariamente, RulesTestEnvironment.withSecurityRulesDisabled .
  • Configurando o conjunto de testes e ganchos por teste antes/depois com chamadas para limpar dados de teste e ambiente, como RulesTestEnvironment.cleanup() ou RulesTestEnvironment.clearFirestore() .
  • Implementação de casos de teste que imitam estados de autenticação usando RulesTestEnvironment.authenticatedContext e RulesTestEnvironment.unauthenticatedContext .

Métodos comuns e funções de utilidade

Consulte também os métodos de teste específicos do emulador no SDK v9 .

initializeTestEnvironment() => RulesTestEnvironment

Esta função inicializa um ambiente de teste para teste de unidade de regras. Chame esta função primeiro para configuração de 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 de projeto e definições de configuração 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 de autenticação autenticado. As solicitações criadas por meio do contexto retornado terão um token de autenticação simulado anexado. Opcionalmente, passe um objeto que defina declarações personalizadas ou substituições para cargas de token de autenticação.

Use o objeto de contexto de teste retornado em seus testes para acessar quaisquer instâncias de emulador configuradas, incluindo aquelas 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 que não está conectado via Autenticação. As solicitações criadas por meio do contexto retornado não terão tokens de autenticação do Firebase anexados.

Use o objeto de contexto de teste retornado em seus testes para acessar quaisquer instâncias de emulador configuradas, incluindo aquelas 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 comporta como se as regras de segurança estivessem desabilitadas.

Esse método usa uma função de retorno de chamada, que usa o contexto de desvio de regras de segurança e retorna uma promessa. O contexto será destruído assim que a promessa for resolvida/rejeitada.

RulesTestEnvironment.cleanup()

Esse método destrói todos os RulesTestContexts criados no ambiente de teste e limpa os recursos subjacentes, permitindo uma saída limpa.

Este método não altera o estado dos emuladores de forma alguma. Para redefinir dados entre testes, use o método clear data específico do emulador do aplicativo.

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

Esta é uma função de utilidade de caso de teste.

A função afirma que a promessa fornecida que envolve uma operação de emulador será resolvida sem violações de regras de segurança.

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

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

Esta é uma função de utilidade de caso de teste.

A função afirma que a promessa fornecida que envolve uma operação de emulador será rejeitada com uma violação de regras de segurança.

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

Métodos específicos do emulador

Consulte também métodos de teste comuns e funções de utilitário no SDK v9 .

RulesTestEnvironment.clearFirestore() => Promise<void>

Este método limpa os dados no banco de dados do Firestore que pertencem ao projectId configurado para o emulador do Firestore.

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

Este método obtém uma instância do Firestore para este contexto de teste. A instância do SDK do cliente Firebase JS retornada pode ser usada com as APIs do SDK do cliente (v9 modular ou compatível com v9).

Visualize avaliações de regras

O emulador do Cloud Firestore permite visualizar solicitações de clientes na IU do Emulator Suite, incluindo rastreamento de avaliação para regras de segurança do Firebase.

Abra a guia Firestore > Solicitações para visualizar a sequência de avaliação detalhada de cada solicitação.

Monitor de solicitações do Firestore Emulator mostrando avaliações de regras de segurança

Gerar relatórios de teste

Depois de executar um conjunto de testes, você pode acessar relatórios de cobertura de teste que mostram como cada uma de suas regras de segurança foi avaliada.

Para obter os relatórios, consulte um ponto de extremidade exposto no emulador enquanto ele estiver em execução. Para uma versão amigável ao navegador, use o seguinte URL:

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

Isso divide suas regras em expressões e subexpressões sobre as quais você pode passar o mouse para obter mais informações, incluindo o número de avaliações e valores retornados. Para a versão 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 explicitamente um projeto do Cloud Firestore. O emulador cria automaticamente qualquer instância acessada.
  2. O emulador do Cloud 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 biblioteca rules-unit-testing , que usa um campo de auth . O identificador do Firebase criado usando esse método se comportará como se tivesse sido autenticado com sucesso como qualquer entidade que você fornecer. Se você passar null , ele se comportará como um usuário não autenticado ( auth != null falharão, por exemplo).

Solucionar problemas conhecidos

Ao usar o emulador do Cloud Firestore, você pode encontrar os seguintes problemas conhecidos. Siga as orientações abaixo para solucionar qualquer comportamento irregular que você esteja enfrentando. Essas notas foram escritas com a biblioteca de testes de unidade de regras de segurança em mente, mas as abordagens gerais são aplicáveis ​​a qualquer SDK do Firebase.

O comportamento do teste é inconsistente

Se seus testes forem aprovados e reprovados ocasionalmente, mesmo sem nenhuma alteração nos próprios testes, talvez seja necessário verificar se eles estão sequenciados corretamente. A maioria das interações com o emulador é assíncrona, portanto, verifique se todo o código assíncrono está sequenciado corretamente. Você pode corrigir o sequenciamento encadeando promessas ou usando a notação await liberalmente.

Em particular, revise as seguintes operações assíncronas:

  • Definir regras de segurança com, por exemplo, initializeTestEnvironment .
  • Lendo e gravando dados, com, por exemplo, db.collection("users").doc("alice").get() .
  • Asserções operacionais, incluindo assertSucceeds e assertFails .

Os testes só passam na primeira vez que você carrega o emulador

O emulador é com estado. Ele armazena todos os dados gravados nele na memória, portanto, todos os dados são perdidos sempre que o emulador é desligado. Se você estiver executando vários testes no mesmo ID de projeto, cada teste pode produzir dados que podem influenciar os testes subsequentes. Você pode usar qualquer um dos seguintes métodos para ignorar esse comportamento:

  • Use IDs de projeto exclusivos para cada teste. Observe que, se você optar por fazer isso, precisará 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 escritos 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, você pode querer modificar os dados de uma forma que suas regras de segurança do Cloud Firestore não permitem. Se suas regras estiverem tornando a configuração de teste complexa, tente usar RulesTestEnvironment.withSecurityRulesDisabled nas etapas de configuração, para que leituras e gravações não acionem erros PERMISSION_DENIED .

Depois disso, seu teste pode realizar operações como um usuário autenticado ou não autenticado usando RulesTestEnvironment.authenticatedContext e unauthenticatedContext , respectivamente. Isso permite validar se suas regras de segurança do Cloud Firestore permitem/negam diferentes casos corretamente.