Criar testes de unidade

O Firebase Local Emulator Suite facilita a validação completa dos recursos e do comportamento do seu aplicativo. 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 devem ajudá-lo a criar e automatizar testes de unidade para seu aplicativo que validam suas regras.

Se ainda não o fez, configure os emuladores do Firebase .

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 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.
  • Embora a maioria dos SDKs do Firebase funcionem diretamente com os emuladores, apenas a biblioteca @firebase/rules-unit-testing oferece suporte à simulação de auth nas regras de segurança, tornando os testes de unidade muito mais fáceis. 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 produção do Firebase Auth fornecidos por meio de SDKs de cliente e avaliarão as regras de acordo, o que permite conectar seu aplicativo diretamente aos emuladores em integração e testes manuais.

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

  • Você não precisa criar explicitamente uma instância de banco de dados. O emulador criará automaticamente qualquer instância de banco de dados acessada.
  • Cada novo banco de dados é iniciado com regras fechadas, portanto, usuários não administradores não poderão ler ou gravar.
  • Cada banco de dados emulado aplica os limites e cotas do plano Spark (mais notavelmente, 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.
  • Atualmente, os emuladores não têm interações funcionais com outros produtos do Firebase. Notavelmente, o fluxo normal do Firebase Authentication não funciona. Em vez disso, você pode usar o método initializeTestApp() na biblioteca rules-unit-testing , que usa um campo auth . O objeto Firebase criado usando esse método se comporta 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 regras falharão, por exemplo).

Interagindo com o emulador do Realtime Database

Uma instância de produção do Firebase Realtime Database pode ser acessada em um subdomínio de firebaseio.com e você pode acessar a API REST assim:

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ê terá que usar o parâmetro de consulta ns para especificar o nome do banco de dados.

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

Execute testes de unidade local com o SDK JavaScript versão 9

O Firebase distribui uma biblioteca de teste de unidade de regras de segurança com seu SDK JavaScript versão 9 e seu SDK 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 conectar-se a emuladores e, assim, evitar com segurança o uso acidental de recursos de produção. Para compatibilidade com versões anteriores, continuamos disponibilizando a biblioteca de testes 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.

Recomendamos fortemente 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 você deseja testar envolve funções assíncronas, e o módulo de teste foi projetado para funcionar com código baseado em Promise.

A biblioteca de testes unitários de regras v9 está sempre ciente dos emuladores e nunca afeta seus recursos de produção.

Você importa a biblioteca usando instruções de importação modular 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.

Depois de importado, a implementação de testes unitários envolve:

  • Criando e configurando um RulesTestEnvironment com uma chamada para initializeTestEnvironment .
  • Configurar 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 antes/depois por teste com chamadas para limpar dados e ambiente de teste, como RulesTestEnvironment.cleanup() ou RulesTestEnvironment.clearFirestore() .
  • Implementar 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

Esta função inicializa um ambiente de teste para testes unitários 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

Este 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 ou substituições personalizadas para cargas úteis de token de autenticação.

Use o objeto de contexto de teste retornado em seus testes para acessar qualquer instância de emulador configurada, 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

Este método cria um RulesTestContext , que se comporta como um cliente que não está logado via autenticação. As solicitações criadas por meio do contexto retornado não terão tokens do Firebase Auth anexados.

Use o objeto de contexto de teste retornado em seus testes para acessar qualquer instância de emulador configurada, 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.

Este método usa uma função de retorno de chamada, que pega 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()

Este 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 de limpeza de dados específico do emulador do aplicativo.

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

Esta é uma função utilitária de caso de teste.

A função afirma que a operação Promise fornecida envolvendo um 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 de caso de teste.

A função afirma que a promessa fornecida envolvendo 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 do emulador

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

Cloud Fire Store

Cloud Fire Store

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

Banco de dados em tempo real

Banco de dados em tempo real

RulesTestEnvironment.clearDatabase() => Promise<void>

Este 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;

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

Armazenamento na núvem

Armazenamento na núvem

RulesTestEnvironment.clearStorage() => Promise<void>

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

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

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

Execute testes de unidade local com o SDK JavaScript v8

Selecione um produto para ver os métodos usados ​​pelo Firebase Test SDK para fazer interface com o emulador.

Cloud Fire Store

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

Este método retorna um aplicativo Firebase inicializado correspondente ao ID do projeto e à variável de autenticação especificada nas opções. Use isto para criar um aplicativo autenticado como um usuário específico para usar em testes.

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

initializeAdminApp({ projectId: string }) => FirebaseApp

Este método retorna um aplicativo Firebase de administrador inicializado. Este aplicativo ignora as regras de segurança ao realizar leituras e gravações. Use isso para criar um aplicativo autenticado como administrador para definir o estado dos testes.

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

apps() => [FirebaseApp] Este método retorna todos os aplicativos de teste e administração atualmente inicializados. Use isto para limpar aplicativos entre ou após os testes.

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

loadFirestoreRules({ projectId: string, rules: Object }) => Promise

Este método envia regras para um banco de dados em execução local. É necessário um objeto que especifica as regras como uma string. Use este 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

Este método retorna uma promessa que será rejeitada se a entrada for bem-sucedida ou se a entrada for rejeitada. Use isto para afirmar se uma leitura ou gravação de banco de dados falha.

firebase.assertFails(app.firestore().collection("private").doc("super-secret-document").get());
    

assertSucceeds(pr: Promise) => Promise

Este método retorna uma promessa que será bem-sucedida se a entrada for bem-sucedida e será rejeitada se a entrada for rejeitada. Use isto para afirmar se uma leitura ou gravação de banco de dados foi bem-sucedida.

firebase.assertSucceeds(app.firestore().collection("public").doc("test-document").get());
    

clearFirestoreData({ projectId: string }) => Promise

Este método limpa todos os dados associados a um projeto específico na instância do Firestore em execução local. Use este método para limpeza após os testes.

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

Banco de dados em tempo real

Banco de dados em tempo real

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

Use isto para criar um aplicativo autenticado como um usuário específico para usar em testes.

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

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

initializeAdminApp({ databaseName: string }) => FirebaseApp

Use isso para criar um aplicativo autenticado como administrador para configurar o estado dos testes.

Retorna um aplicativo admin firebase inicializado correspondente ao nome do banco de dados especificado nas opções. Este aplicativo ignora as regras de segurança ao ler e gravar no banco de dados.

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

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

Use isto para definir as regras do seu banco de dados.

Envia regras para um banco de dados em execução local. Pega um objeto de opções que especifica seu "databaseName" e suas "regras" como strings.

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

apps() => [FirebaseApp]

Retorna todos os aplicativos de teste e administração atualmente inicializados.

Use isto para limpar aplicativos entre ou após os testes (observe que aplicativos inicializados com ouvintes ativos impedem a saída do JavaScript):

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

assertFails(pr: Promise) => Promise

Retorna uma promessa que será rejeitada se a entrada for bem-sucedida e se a entrada for rejeitada.

Use isto para afirmar que uma leitura ou gravação de banco de dados falha:

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

assertSucceeds(pr: Promise) => Promise

Retorna uma promessa que será bem-sucedida se a entrada for bem-sucedida e será rejeitada se a entrada for rejeitada.

Use isto para afirmar que uma leitura ou gravação de banco de dados foi bem-sucedida:

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

Armazenamento na núvem

Armazenamento na núvem

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

Use isto para criar um aplicativo autenticado como um usuário específico para usar em testes.

Retorna um aplicativo 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 isso para criar um aplicativo autenticado como administrador para configurar o estado dos testes.

Retorna um aplicativo admin firebase inicializado correspondente ao nome do bucket de armazenamento especificado nas opções. Este aplicativo ignora as regras de segurança ao ler e gravar no bucket.

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

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

Use isto para definir as regras do seu intervalo de armazenamento.

Envia regras para buckets de armazenamento gerenciados localmente. Pega um objeto de opções que especifica seu "storageBucket" e suas "regras" como strings.

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

apps() => [FirebaseApp]

Retorna todos os aplicativos de teste e administração atualmente inicializados.

Use isto para limpar aplicativos entre ou após os testes (observe que aplicativos inicializados com ouvintes ativos impedem a saída do JavaScript):

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

assertFails(pr: Promise) => Promise

Retorna uma promessa que será rejeitada se a entrada for bem-sucedida e se a entrada for rejeitada.

Use isto para afirmar que a leitura ou gravação de um bucket de armazenamento falha:

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

assertSucceeds(pr: Promise) => Promise

Retorna uma promessa que será bem-sucedida se a entrada for bem-sucedida e será rejeitada se a entrada for rejeitada.

Use isto para afirmar que a leitura ou gravação de um bucket de armazenamento foi bem-sucedida:

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

API da biblioteca RUT para JS SDK v8

Selecione um produto para ver os métodos usados ​​pelo Firebase Test SDK para fazer interface com o emulador.

Cloud Fire Store

Cloud Fire Store

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

Este método retorna um aplicativo Firebase inicializado correspondente ao ID do projeto e à variável de autenticação especificada nas opções. Use isto para criar um aplicativo autenticado como um usuário específico para usar em testes.

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

initializeAdminApp({ projectId: string }) => FirebaseApp

Este método retorna um aplicativo Firebase de administrador inicializado. Este aplicativo ignora as regras de segurança ao realizar leituras e gravações. Use isso para criar um aplicativo autenticado como administrador para definir o estado dos testes.

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

apps() => [FirebaseApp] Este método retorna todos os aplicativos de teste e administração atualmente inicializados. Use isto para limpar aplicativos entre ou após os testes.

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

loadFirestoreRules({ projectId: string, rules: Object }) => Promise

Este método envia regras para um banco de dados em execução local. É necessário um objeto que especifica as regras como uma string. Use este 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

Este método retorna uma promessa que será rejeitada se a entrada for bem-sucedida ou se a entrada for rejeitada. Use isto para afirmar se uma leitura ou gravação de banco de dados falha.

firebase.assertFails(app.firestore().collection("private").doc("super-secret-document").get());
    

assertSucceeds(pr: Promise) => Promise

Este método retorna uma promessa que será bem-sucedida se a entrada for bem-sucedida e será rejeitada se a entrada for rejeitada. Use isto para afirmar se uma leitura ou gravação de banco de dados foi bem-sucedida.

firebase.assertSucceeds(app.firestore().collection("public").doc("test-document").get());
    

clearFirestoreData({ projectId: string }) => Promise

Este método limpa todos os dados associados a um projeto específico na instância do Firestore em execução local. Use este método para limpeza após os testes.

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

Banco de dados em tempo real

Banco de dados em tempo real

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

Use isto para criar um aplicativo autenticado como um usuário específico para usar em testes.

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

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

initializeAdminApp({ databaseName: string }) => FirebaseApp

Use isso para criar um aplicativo autenticado como administrador para configurar o estado dos testes.

Retorna um aplicativo admin firebase inicializado correspondente ao nome do banco de dados especificado nas opções. Este aplicativo ignora as regras de segurança ao ler e gravar no banco de dados.

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

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

Use isto para definir as regras do seu banco de dados.

Envia regras para um banco de dados em execução local. Pega um objeto de opções que especifica seu "databaseName" e suas "regras" como strings.

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

apps() => [FirebaseApp]

Retorna todos os aplicativos de teste e administração atualmente inicializados.

Use isto para limpar aplicativos entre ou após os testes (observe que aplicativos inicializados com ouvintes ativos impedem a saída do JavaScript):

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

assertFails(pr: Promise) => Promise

Retorna uma promessa que será rejeitada se a entrada for bem-sucedida e se a entrada for rejeitada.

Use isto para afirmar que uma leitura ou gravação de banco de dados falha:

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

assertSucceeds(pr: Promise) => Promise

Retorna uma promessa que será bem-sucedida se a entrada for bem-sucedida e será rejeitada se a entrada for rejeitada.

Use isto para afirmar que uma leitura ou gravação de banco de dados foi bem-sucedida:

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

Armazenamento na núvem

Armazenamento na núvem

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

Use isto para criar um aplicativo autenticado como um usuário específico para usar em testes.

Retorna um aplicativo 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 isso para criar um aplicativo autenticado como administrador para configurar o estado dos testes.

Retorna um aplicativo admin firebase inicializado correspondente ao nome do bucket de armazenamento especificado nas opções. Este aplicativo ignora as regras de segurança ao ler e gravar no bucket.

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

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

Use isto para definir as regras do seu intervalo de armazenamento.

Envia regras para buckets de armazenamento gerenciados localmente. Pega um objeto de opções que especifica seu "storageBucket" e suas "regras" como strings.

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

apps() => [FirebaseApp]

Retorna todos os aplicativos de teste e administração atualmente inicializados.

Use isto para limpar aplicativos entre ou após os testes (observe que aplicativos inicializados com ouvintes ativos impedem a saída do JavaScript):

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

assertFails(pr: Promise) => Promise

Retorna uma promessa que será rejeitada se a entrada for bem-sucedida e se a entrada for rejeitada.

Use isto para afirmar que a leitura ou gravação de um bucket de armazenamento falha:

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

assertSucceeds(pr: Promise) => Promise

Retorna uma promessa que será bem-sucedida se a entrada for bem-sucedida e será rejeitada se a entrada for rejeitada.

Use isto para afirmar que a leitura ou gravação de um bucket de armazenamento foi bem-sucedida:

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