単体テストの作成

Firebase エミュレータを使用すると、アプリの動作の完全な検証を簡単にできます。また、Firebase セキュリティ ルールの構成も確認しやすくなります。Firebase エミュレータを使用して、ローカル環境で単体テストの実施と自動化を行います。アプリのセキュリティ ルールを検証する単体テストを作成して自動化する際は、このドキュメントで説明するメソッドが便利です。

まだ設定していない場合は、Firebase エミュレータを設定します。

エミュレータを実行する前に

エミュレータを使用する前に、次の点に注意してください。

  • 現在エミュレータをサポートしている唯一の SDK は Node.js SDK です。エミュレータとのやりとりを容易にするために、@firebase/testing モジュールを用意しました。
  • エミュレータはオプションの --rules CLI フラグをサポートしています。ルールが含まれているローカル ファイルの名前をこのフラグに指定すると、そのセキュリティ ルールがすべてのプロジェクトに適用されます。ローカル ファイルのパスを指定しない場合、あるいは loadFirestoreRules メソッド、または loadDatabaseRules メソッドを使用しない場合は、エミュレータはすべてのプロジェクトを、オープンルールが適用されるものとして扱います。

エミュレータと本番環境の違い

  • データベース インスタンスを明示的に作成する必要がありません。エミュレータは、アクセス対象のデータベース インスタンスを自動的に作成します。
  • 新しいデータベースはクローズド ルールを使用して起動されるため、管理者以外のユーザーが読み取りや書き込みを行うことはできません。
  • エミュレートされた各データベースには、Spark プランの制限と割り当てが適用されます(特に重要なのは、これにより各インスタンスの同時接続数が 100 に制限されることです)。
  • どのデータベースも、文字列 "owner" を管理者認証トークンとして受け入れます。
  • 現在のところ、エミュレータは他の Firebase プロダクトと動的なやり取りを行いません。特に、通常の Firebase Authentication フローは機能しません。代わりに、auth フィールドを取るテスト モジュールでは initializeTestApp() メソッドを使用できます。このメソッドを使用して作成された Firebase オブジェクトは、どのようなエンティティを指定しても正常に認証されたかのように動作します。null を渡すと、認証されていないユーザーとして動作します(たとえば auth != null ルールは失敗します)。

ローカルテストを実行する

@firebase/testing モジュールを使用して、ローカルで動作するエミュレータを操作します。タイムアウトまたは ECONNREFUSED エラーが発生する場合は、エミュレータが実行されているか確認してください。

async/await の表記法を使用できるようにするため、新しいバージョンの Node.js を使用することを強くおすすめします。テスト対象となる可能性のある動作のほとんどには非同期関数があります。また、テスト モジュールは Promise ベースのコードで動作するように設計されています。

エミュレータのメソッド

エミュレータ モジュールが公開しているメソッドを確認するプロダクトを選択してください。

Cloud Firestore

Cloud Firestore

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

このメソッドは、オプションで指定されたプロジェクト ID と auth 変数に対応する、初期化された Firebase アプリを返します。テストで使用するために、特定のユーザーとして認証されたアプリを作成するには、このメソッドを次のように使用します。

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

initializeAdminApp({ projectId: string }) => FirebaseApp

このメソッドは、初期化された Firebase 管理アプリを返します。このアプリは、読み取りと書き込みを行う際にセキュリティ ルールを迂回します。テストの状態を設定するために、管理者として認証されたアプリを作成するには、このメソッドを次のように使用します。

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

apps() => [FirebaseApp] このメソッドは、現在初期化されているテストと管理アプリをすべて返します。テスト間またはテスト後にアプリを消去するには、このメソッドを次のように使用します。

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

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

このメソッドは、ローカルで実行されているデータベースにルールを送信します。ルールを文字列として指定するオブジェクトを取ります。データベースのルールを設定するには、このメソッドを次のように使用します。

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

assertFails(pr: Promise) => Promise

このメソッドは、入力が成功した場合は拒否され、入力が拒否された場合は成功する Promise を返します。データベースの読み取りや書き込みが失敗したかどうかをアサートするには、このメソッドを次のように使用します。

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

assertSucceeds(pr: Promise) => Promise

このメソッドは、入力が成功した場合は成功し、入力が拒否された場合は拒否される Promise を返します。データベースの読み取りや書き込みが成功したかどうかをアサートするには、このメソッドを次のように使用します。

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

clearFirestoreData({ projectId: string }) => Promise

このメソッドは、ローカルで実行中の Firestore インスタンス内の特定のプロジェクトに関連付けられているすべてのデータを消去します。テスト後のクリーンアップには、このメソッドを次のように使用します。

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

Realtime Database

Realtime Database

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

テストで使用するために、特定のユーザーとして認証されたアプリを作成するには、このメソッドを次のように使用します。

オプションで指定されたデータベース名と auth 変数のオーバーライドに対応する、初期化された Firebase アプリを返します。

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

initializeAdminApp({ databaseName: string }) => FirebaseApp

テストの状態を設定するために、管理者として認証されたアプリを作成するには、このメソッドを次のように使用します。

オプションで指定されたデータベース名に対応する初期化された Firebase 管理アプリを返します。このアプリは、データベースに対する読み書きを行う際にセキュリティ ルールを迂回します。

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

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

データベースのルールを設定するには、このメソッドを次のように使用します。

ローカルで実行されているデータベースにルールを送信します。"databaseName" と "rules" を文字列として指定したオプション オブジェクトを取ります。

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

apps() => [FirebaseApp]

現在初期化されているテストと管理アプリをすべて返します。

テスト間またはテスト後にアプリを消去するには、このメソッドを次のように使用します(初期化済みのアプリでアクティブなリスナーを持つ場合は、JavaScript を終了できないのでご注意ください)。

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

assertFails(pr: Promise) => Promise

入力が成功した場合は拒否され、入力が拒否された場合は成功する Promise を返します。

データベースの読み取りや書き込みに失敗したことをアサートするには、このメソッドを次のように使用します。

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

assertSucceeds(pr: Promise) => Promise

入力が成功した場合は成功し、入力が拒否された場合は拒否される Promise を返します。

データベースの読み取りや書き込みが成功したことをアサートするには、このメソッドを次のように使用します。

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