O Google tem o compromisso de promover a igualdade racial para as comunidades negras. Saiba como.

Testar as 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 exemplo do guia de início rápido.

Entender 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 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 CLI do Firebase 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 arquivo firebase.json. Ele espera receber o nome de um arquivo local com 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.
  • Enquanto a maioria dos SDKs do Firebase trabalha com emuladores diretamente, somente a biblioteca @firebase/rules-unit-testing é compatível com a simulação de auth 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 aceitarão tokens de produção do Firebase Authentication, fornecidos pelos SDKs do cliente e avaliarão as regras, o que permite conectar seu aplicativo diretamente aos emuladores em testes manuais e de integração.

Executar testes locais

initializeTestApp({ projectId: string, auth: Object }) => FirebaseApp

Esse método retorna um aplicativo do Firebase inicializado que corresponde ao ID do projeto e à variável de autenticação especificados nas opções. Use esse método e crie um app autenticado como um usuário específico para ser utilizado em testes.

firebase.initializeTestApp({
  projectId: "my-test-project",
  auth: { uid: "alice", email: "alice@example.com" }
});

initializeAdminApp({ projectId: string }) => FirebaseApp

Esse método retorna um aplicativo de administrador inicializado do Firebase. As regras de segurança são ignoradas quando esse aplicativo executa leituras e gravações. Use-o para criar um app autenticado como administrador e definir o estado para os testes.

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

apps() => [FirebaseApp] Esse método retorna todos os apps de teste e administração inicializados e em execução no momento. Ele pode ser usado para limpar aplicativos entre ou após os testes.

Promise.all(firebase.apps().map(app => app.delete()))

loadFirestoreRules({ projectId: string, rules: Object }) => 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-o 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. Use-o para informar se ocorreu uma falha na leitura ou 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 que será recusada se a entrada for recusada. Use-o 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: string }) => 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 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 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 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 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 auth. 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ê. Caso null seja transmitido, ele se comportará como um usuário não autenticado. Por exemplo, as regras auth != null falharão.

Resolver problemas conhecidos

Ao usar o emulador do Cloud Firestore, talvez você se depare com os problemas conhecidos a seguir. Siga as orientações abaixo para resolver qualquer comportamento irregular que ocorrer. Essas observações foram escritas pensando no SDK de teste do Firebase, 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, firebase.loadFirestoreRules.
  • Ler e gravar dados com, por exemplo, db.collection("users").doc("alice").get().
  • Declaraçõ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 IDs de projetos exclusivos para cada teste. Se você optar por fazer isso, será necessário chamar loadFirestoreRules 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

Pode ser necessário testar cenários que suas regras de segurança do 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 ignorá-las. Faça isso com firebase.initializeAdminApp. Leituras e gravações de clientes autorizados pelo administrador ignoram regras e não acionam erros PERMISSION_DENIED.