Firebase エミュレータを使用すると、アプリの動作の完全な検証を簡単にできます。また、Firebase セキュリティ ルールの構成も確認しやすくなります。Firebase エミュレータを使用して、ローカル環境で単体テストの実施と自動化を行います。アプリのセキュリティ ルールを検証する単体テストを作成して自動化する際は、このドキュメントで説明するメソッドが便利です。
まだ設定していない場合は、Firebase エミュレータを設定します。
エミュレータを実行する前に
エミュレータを使用する前に、次の点に注意してください。
- エミュレータは最初に
firebase.json
ファイルのfirestore.rules
フィールドで指定されたルールを読み込みます。ファイルが存在せず、次で説明するようにloadFirestoreRules
メソッドを使用しない場合、エミュレータはすべてのプロジェクトをオープンルールが適用されるものとして扱います。 - ほとんどの Firebase SDK はエミュレータで直接動作しますが、セキュリティ ルールで
auth
の擬似的再現をサポートしているのは@firebase/rules-unit-testing
ライブラリのみです。したがって、このライブラリでは単体テストがはるかに簡単になります。また、このライブラリは以下にリストされているエミュレータ固有の機能(すべてのデータのクリアなど)もサポートしています。 - エミュレータは、クライアント SDK から提供される本番環境の Firebase Auth トークンも受け入れ、それに応じてルールを評価します。そのため、統合テストと手動テストでアプリケーションをエミュレータに直接接続できます。
エミュレータと本番環境の違い
- データベース インスタンスを明示的に作成する必要がありません。エミュレータは、アクセス対象のデータベース インスタンスを自動的に作成します。
- 新しいデータベースはクローズド ルールを使用して起動されるため、管理者以外のユーザーが読み取りや書き込みを行うことはできません。
- エミュレートされた各データベースには、Spark プランの制限と割り当てが適用されます(特に重要なのは、これにより各インスタンスの同時接続数が 100 に制限されることです)。
- どのデータベースも、文字列
"owner"
を管理者認証トークンとして受け入れます。 - 現在のところ、エミュレータは他の Firebase プロダクトと機能するやり取りを行いません。特に、通常の Firebase Authentication フローは機能しません。代わりに、
rules-unit-testing
ライブラリのinitializeTestApp()
メソッドを使用できます。このメソッドはauth
フィールドを受け取ります。このメソッドを使用して作成された Firebase オブジェクトは、どのようなエンティティを指定しても正常に認証されたかのように動作します。null
を渡すと、認証されていないユーザーとして動作します(たとえばauth != null
ルールは失敗します)。
ローカルテストを実行する
@firebase/rules-unit-testing
モジュールを使用して、ローカルで動作するエミュレータを操作します。タイムアウトまたは ECONNREFUSED
エラーが発生する場合は、エミュレータが実行されていることを確認してください。
async/await
の表記法を使用できるようにするため、新しいバージョンの Node.js を使用することを強くおすすめします。テスト対象となる可能性のある動作のほとんどには非同期関数があります。また、テスト モジュールは Promise ベースのコードで動作するように設計されています。
Realtime Database エミュレータを操作する
本番環境の Firebase Realtime Database インスタンスは、firebaseio.com
のサブドメインからアクセスできるため、次の方法で REST API にアクセスできます。
https://<database_name>.firebaseio.com/path/to/my/data.json
エミュレータはローカルで実行され、localhost:9000
でアクセスできます。特定のデータベース インスタンスと対話するには、ns
を使用してデータベース名を指定する必要があります。
http://localhost:9000/path/to/my/data.json?ns=<database_name>
SDK のメソッドをテストする
プロダクトを選択すると、Firebase Test SDK によるエミュレータの操作に使用されるメソッドが表示されます。
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"));