Criar testes de unidade

O Pacote de emuladores locais do Firebase facilita a validação completa dos recursos e comportamento do app. Também é uma ótima ferramenta para verificar as configurações das regras de segurança do Firebase. Use os emuladores do Firebase para executar e automatizar testes de unidade em um ambiente local. Os métodos descritos neste documento vão ajudar você a criar e automatizar testes de unidade que validem suas regras do app.

Configure os emuladores do Firebase caso ainda não tenha feito isso.

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 ou "storage.rules" do seu arquivo firebase.json. Se o arquivo não existir e você não usar o método loadFirestoreRules ou "loadStorageRules", conforme descrito abaixo, o emulador tratará todos os projetos como tendo regras abertas.
  • Enquanto a maioria dos SDKs do Firebase funcionam com emuladores diretamente, somente a biblioteca @firebase/rules-unit-testing tem suporte à simulação de auth nas regras de segurança, o que facilita muito os testes de unidade. Além disso, a biblioteca tem suporte a 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 clientes e avaliarão as regras, o que permite conectar o aplicativo diretamente aos emuladores em testes manuais e de integração.

Diferenças entre os emuladores de banco de dados e a produção

  • Você não precisa criar uma instância de banco de dados de maneira explícita. O emulador criará automaticamente qualquer instância como essa que for acessada.
  • Cada novo banco de dados é iniciado com regras fechadas. Dessa forma, usuários que não forem administradores não terão permissão para realizar operações de leitura ou gravação.
  • Cada banco de dados emulado aplica os limites e as cotas do plano Spark (observe que isso limita cada instância a 100 conexões simultâneas).
  • Qualquer banco de dados aceitará a string "owner" como um token de autenticação de administrador.
  • No momento, os emuladores não têm interações de trabalho com outros produtos do Firebase. Além disso, é importante ressaltar que o Firebase Authentication não funcionará no fluxo normal. Em vez disso, é possível usar o método initializeTestApp() na biblioteca rules-unit-testing, que usa um campo auth. O objeto do Firebase criado por meio desse método se comportará como se tivesse sido autenticado com sucesso como qualquer entidade que você fornecer. Caso null seja transmitido, ele se comportará como um usuário não autenticado. Por exemplo, as regras auth != null falharão.

Como interagir com o emulador do Realtime Database

É possível acessar uma instância de produção do Firebase Realtime Database usando um subdomínio de firebaseio.com. Acesse a API REST usando o seguinte comando:

https://<database_name>.firebaseio.com/path/to/my/data.json

O emulador é executado localmente e está disponível em localhost:9000. Para interagir com uma instância de banco de dados específica, você precisará usar um parâmetro de consulta ns a fim de especificar o nome desse banco de dados.

http://localhost:9000/path/to/my/data.json?ns=<database_name>

Executar testes de unidade locais com o SDK para JavaScript versão 9

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.

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,
  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.

Depois de importados, a implementação de testes de unidade envolve:

  • a criação e a configuração de um RulesTestEnvironment com uma chamada para initializeTestEnvironment;
  • 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() ou RulesTestEnvironment.clearFirestore();
  • a implementação de casos de teste que imitam estados de autenticação usando RulesTestEnvironment.authenticatedContext e RulesTestEnvironment.unauthenticatedContext.

Métodos comuns e funções utilitárias

Consulte também métodos de teste específicos do emulador usando a API modular.

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

Confira também métodos de teste comuns e funções utilitárias usando a API modular.

Cloud Firestore

Cloud Firestore

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 cliente do Firebase para JS pode ser usada com as APIs do SDK cliente (v9 modular ou v9 compatível).

Realtime Database

Realtime Database

RulesTestEnvironment.clearDatabase() => Promise<void>

Esse método limpa os dados no Realtime Database que pertencem ao projectId configurado para o emulador do Realtime Database.

RulesTestContext.database(databaseURL?: Firestore.FirestoreSettings) => Firestore;

Consiga uma instância do Realtime Database para este contexto de teste. A instância retornada do SDK do cliente do Firebase para JavaScript pode ser usada com as APIs do SDK do cliente (modular ou namespace, versão 9 ou posterior). O método aceita um URL da instância do Realtime Database. Se especificado, retorna uma instância de uma versão emulada do namespace com parâmetros extraídos do URL.

Cloud Storage

Cloud Storage

RulesTestEnvironment.clearStorage() => Promise<void>

Esse método limpa objetos e metadados em buckets de armazenamento pertencentes ao projectId configurado para o emulador do Cloud Storage.

RulesTestContext.storage(bucketUrl?: string) => Firebase Storage;

Esse método retorna uma instância do Storage configurada para se conectar ao emulador. O método aceita um URL gs:// para o bucket do Firebase Storage para testes. Se especificado, retorna uma instância do Storage para uma versão emulada do nome do bucket.

Executar testes de unidade locais com o SDK para JavaScript v8

Selecione um produto para ver os métodos usados pelo SDK de teste do Firebase a fim de interagir com o emulador.

Cloud Firestore

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"
});
   

Realtime Database

Realtime Database

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

Use esse método para criar um aplicativo autenticado como um usuário específico a ser utilizado em testes.

Ele retorna um aplicativo do Firebase inicializado correspondente ao nome do banco de dados e à modificação da variável de autenticação especificada nas opções.

firebase.initializeTestApp({
  databaseName: "my-database",
  auth: { uid: "alice" }
});

initializeAdminApp({ databaseName: string }) => FirebaseApp

Use esse método para criar um app autenticado como administrador e configurar o estado para os testes.

Ele retorna um aplicativo de administrador do Firebase inicializado correspondente ao nome do banco de dados especificado nas opções. Esse aplicativo ignora as regras de segurança durante a leitura e a gravação no banco de dados.

firebase.initializeAdminApp({ databaseName: "my-database" });

loadDatabaseRules({ databaseName: string, rules: Object }) => Promise

Use esse método para definir as regras do seu banco de dados.

Ele envia regras para um banco de dados executado localmente. Além disso, ele usa um objeto de opções que especifica "databaseName" e "rules" como strings.

firebase
      .loadDatabaseRules({
        databaseName: "my-database",
        rules: "{'rules': {'.read': false, '.write': false}}"
      });

apps() => [FirebaseApp]

Esse método retorna todos os aplicativos de teste e de administrador inicializados no momento.

Use-o para limpar aplicativos durante e depois dos testes. Observação: os aplicativos inicializados com listeners ativos impedem o JavaScript de finalizar a execução:

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

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 que ocorreu uma falha na leitura ou na gravação no banco de dados:

firebase.assertFails(app.database().ref("secret").once("value"));

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.

Use-o para informar que uma leitura ou gravação no banco de dados foi concluída:

firebase.assertSucceeds(app.database().ref("public").once("value"));

Cloud Storage

Cloud Storage

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

Use esse método para criar um aplicativo autenticado como um usuário específico a ser utilizado em testes.

Retorna um app do Firebase inicializado correspondente ao nome do bucket de armazenamento e à substituição da variável de autenticação especificada nas opções.

firebase.initializeTestApp({
  storageBucket: "my-bucket",
  auth: { uid: "alice" }
});

initializeAdminApp({ storageBucket: string }) => FirebaseApp

Use esse método para criar um app autenticado como administrador e configurar o estado para os testes.

Retorna um app do Firebase de administrador inicializado que corresponde ao nome do bucket de armazenamento especificado nas opções. Esse aplicativo ignora as regras de segurança durante a leitura e a gravação no bucket.

firebase.initializeAdminApp({ storageBucket: "my-bucket" });

loadStorageRules({ storageBucket: string, rules: Object }) => Promise

Use-o para definir as regras do bucket de armazenamento.

Envia regras para buckets de armazenamento gerenciados localmente. Além disso, ele usa um objeto de opções que especifica "storageBucket" e "rules" como strings.

firebase
      .loadStorageRules({
        storageBucket: "my-bucket",
        rules: fs.readFileSync("/path/to/storage.rules", "utf8")
      });

apps() => [FirebaseApp]

Esse método retorna todos os aplicativos de teste e de administrador inicializados no momento.

Use-o para limpar aplicativos durante e depois dos testes. Observação: os aplicativos inicializados com listeners ativos impedem o JavaScript de finalizar a execução:

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

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 bucket de armazenamento:

firebase.assertFails(app.storage().ref("letters/private.doc").getMetadata());

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.

Use-o para informar a conclusão de uma leitura ou gravação no bucket de armazenamento:

firebase.assertFails(app.storage().ref("images/cat.png").getMetadata());

API RUT Library para o SDK JS v8

Selecione um produto para ver os métodos usados pelo SDK de teste do Firebase a fim de interagir com o emulador.

Cloud Firestore

Cloud Firestore

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"
});
   

Realtime Database

Realtime Database

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

Use esse método para criar um aplicativo autenticado como um usuário específico a ser utilizado em testes.

Ele retorna um aplicativo do Firebase inicializado correspondente ao nome do banco de dados e à modificação da variável de autenticação especificada nas opções.

firebase.initializeTestApp({
  databaseName: "my-database",
  auth: { uid: "alice" }
});

initializeAdminApp({ databaseName: string }) => FirebaseApp

Use esse método para criar um app autenticado como administrador e configurar o estado para os testes.

Ele retorna um aplicativo de administrador do Firebase inicializado correspondente ao nome do banco de dados especificado nas opções. Esse aplicativo ignora as regras de segurança durante a leitura e a gravação no banco de dados.

firebase.initializeAdminApp({ databaseName: "my-database" });

loadDatabaseRules({ databaseName: string, rules: Object }) => Promise

Use esse método para definir as regras do seu banco de dados.

Ele envia regras para um banco de dados executado localmente. Além disso, ele usa um objeto de opções que especifica "databaseName" e "rules" como strings.

firebase
      .loadDatabaseRules({
        databaseName: "my-database",
        rules: "{'rules': {'.read': false, '.write': false}}"
      });

apps() => [FirebaseApp]

Esse método retorna todos os aplicativos de teste e de administrador inicializados no momento.

Use-o para limpar aplicativos durante e depois dos testes. Observação: os aplicativos inicializados com listeners ativos impedem o JavaScript de finalizar a execução:

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

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 que ocorreu uma falha na leitura ou na gravação no banco de dados:

firebase.assertFails(app.database().ref("secret").once("value"));

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.

Use-o para informar que uma leitura ou gravação no banco de dados foi concluída:

firebase.assertSucceeds(app.database().ref("public").once("value"));

Cloud Storage

Cloud Storage

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

Use esse método para criar um aplicativo autenticado como um usuário específico a ser utilizado em testes.

Retorna um app do Firebase inicializado correspondente ao nome do bucket de armazenamento e à substituição da variável de autenticação especificada nas opções.

firebase.initializeTestApp({
  storageBucket: "my-bucket",
  auth: { uid: "alice" }
});

initializeAdminApp({ storageBucket: string }) => FirebaseApp

Use esse método para criar um app autenticado como administrador e configurar o estado para os testes.

Retorna um app do Firebase de administrador inicializado que corresponde ao nome do bucket de armazenamento especificado nas opções. Esse aplicativo ignora as regras de segurança durante a leitura e a gravação no bucket.

firebase.initializeAdminApp({ storageBucket: "my-bucket" });

loadStorageRules({ storageBucket: string, rules: Object }) => Promise

Use-o para definir as regras do bucket de armazenamento.

Envia regras para buckets de armazenamento gerenciados localmente. Além disso, ele usa um objeto de opções que especifica "storageBucket" e "rules" como strings.

firebase
      .loadStorageRules({
        storageBucket: "my-bucket",
        rules: fs.readFileSync("/path/to/storage.rules", "utf8")
      });

apps() => [FirebaseApp]

Esse método retorna todos os aplicativos de teste e de administrador inicializados no momento.

Use-o para limpar aplicativos durante e depois dos testes. Observação: os aplicativos inicializados com listeners ativos impedem o JavaScript de finalizar a execução:

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

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 bucket de armazenamento:

firebase.assertFails(app.storage().ref("letters/private.doc").getMetadata());

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.

Use-o para informar a conclusão de uma leitura ou gravação no bucket de armazenamento:

firebase.assertFails(app.storage().ref("images/cat.png").getMetadata());