Firebase Local Emulator Suite 可以更轻松地全面验证您应用的功能和行为。它也是验证 Firebase 安全规则配置的绝佳工具。使用 Firebase 模拟器在本地环境中运行和自动化单元测试。本文档中概述的方法应该可以帮助您为验证规则的应用程序构建和自动化单元测试。
如果您还没有设置 Firebase Emulators 。
运行模拟器之前
在开始使用模拟器之前,请记住以下几点:
- 模拟器最初会加载在
firebase.json
文件的firestore.rules
或“storage.rules”字段中指定的规则。如果该文件不存在并且您没有使用如下所述的loadFirestoreRules
或“loadStorageRules”方法,则模拟器会将所有项目视为具有开放规则。 - 虽然大多数 Firebase SDK直接与模拟器一起使用,但只有
@firebase/rules-unit-testing
库支持安全规则中的模拟auth
,从而使单元测试更加容易。此外,该库还支持一些特定于模拟器的功能,例如清除所有数据,如下所列。 - 模拟器还将接受通过客户端 SDK 提供的生产 Firebase Auth 令牌并相应地评估规则,这允许您的应用程序在集成和手动测试中直接连接到模拟器。
数据库模拟器和生产环境之间的差异
- 您不必显式创建数据库实例。模拟器将自动创建任何被访问的数据库实例。
- 每个新数据库都以封闭规则启动,因此非管理员用户将无法读取或写入。
- 每个模拟数据库都应用Spark 计划限制和配额(最值得注意的是,这将每个实例限制为 100 个并发连接)。
- 任何数据库都将接受字符串
"owner"
作为管理员身份验证令牌。 - 模拟器目前没有与其他 Firebase 产品的工作交互。值得注意的是,正常的 Firebase 身份验证流程不起作用。相反,您可以使用
rules-unit-testing
库中的initializeTestApp()
方法,该方法采用auth
字段。使用此方法创建的 Firebase 对象的行为就像它已成功验证为您提供的任何实体一样。如果您传入null
,它将表现为未经身份验证的用户(例如,auth != null
规则将失败)。
与实时数据库模拟器交互
可以在 firebaseio.com 的子域访问生产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>
使用第 9 版 JavaScript SDK 运行本地单元测试
Firebase 分发了一个安全规则单元测试库,其中包含第 9 版 JavaScript SDK 和第 8 版 SDK。库 API 明显不同。我们推荐 v9 测试库,它更加精简并且需要更少的设置来连接到模拟器,从而安全地避免意外使用生产资源。为了向后兼容,我们继续提供v8 测试库。
使用@firebase/rules-unit-testing
模块与本地运行的模拟器交互。如果出现超时或ECONNREFUSED
错误,请仔细检查模拟器是否确实在运行。
我们强烈建议使用最新版本的 Node.js,以便您可以使用async/await
表示法。您可能想要测试的几乎所有行为都涉及异步函数,并且测试模块旨在与基于 Promise 的代码一起使用。
v9 规则单元测试库始终了解模拟器并且从不接触您的生产资源。
您使用 v9 模块化导入语句导入库。例如:
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.
导入后,实施单元测试涉及:
- 通过调用
initializeTestEnvironment
创建和配置RulesTestEnvironment
。 - 在不触发规则的情况下设置测试数据,使用允许您暂时绕过它们的便捷方法
RulesTestEnvironment.withSecurityRulesDisabled
。 - 设置测试套件和每个测试之前/之后挂钩,调用清理测试数据和环境,如
RulesTestEnvironment.cleanup()
或RulesTestEnvironment.clearFirestore()
。 - 使用
RulesTestEnvironment.authenticatedContext
和RulesTestEnvironment.unauthenticatedContext
实现模拟身份验证状态的测试用例。
常用方法和实用函数
另请参阅v9 SDK 中特定于模拟器的测试方法。
initializeTestEnvironment() => RulesTestEnvironment
此函数为规则单元测试初始化测试环境。首先调用此函数进行测试设置。成功执行需要运行模拟器。
该函数接受定义TestEnvironmentConfig
的可选对象,该对象可以包含项目 ID 和模拟器配置设置。
let testEnv = await initializeTestEnvironment({ projectId: "demo-project-1234", firestore: { rules: fs.readFileSync("firestore.rules", "utf8"), }, });
RulesTestEnvironment.authenticatedContext({ user_id: string, tokenOptions?: TokenOptions }) => RulesTestContext
此方法创建一个RulesTestContext
,其行为类似于经过身份验证的身份验证用户。通过返回的上下文创建的请求将附加一个模拟身份验证令牌。或者,传递一个定义自定义声明或覆盖身份验证令牌有效负载的对象。
在测试中使用返回的测试上下文对象来访问配置的任何模拟器实例,包括使用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
此方法创建一个RulesTestContext
,其行为类似于未通过身份验证登录的客户端。通过返回的上下文创建的请求不会附加 Firebase Auth 令牌。
在测试中使用返回的测试上下文对象来访问配置的任何模拟器实例,包括使用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()
使用上下文运行测试设置函数,其行为就好像安全规则被禁用一样。
此方法采用回调函数,该函数采用绕过安全规则的上下文并返回承诺。一旦承诺解决/拒绝,上下文将被销毁。
RulesTestEnvironment.cleanup()
此方法会销毁在测试环境中创建的所有RulesTestContexts
并清理底层资源,从而允许干净退出。
此方法不会以任何方式更改模拟器的状态。要在测试之间重置数据,请使用特定于应用程序模拟器的清除数据方法。
assertSucceeds(pr: Promise<any>)) => Promise<any>
这是一个测试用例效用函数。
该函数断言所提供的包装模拟器操作的 Promise 将在不违反安全规则的情况下得到解决。
await assertSucceeds(setDoc(alice.firestore(), '/users/alice'), { ... });
assertFails(pr: Promise<any>)) => Promise<any>
这是一个测试用例效用函数。
该函数断言所提供的包装模拟器操作的 Promise 将因违反安全规则而被拒绝。
await assertFails(setDoc(alice.firestore(), '/users/bob'), { ... });
特定于模拟器的方法
另请参阅v9 SDK 中的常见测试方法和实用函数。
云端 Firestore
云端 Firestore
RulesTestEnvironment.clearFirestore() => Promise<void>
此方法清除 Firestore 数据库中属于为 Firestore 模拟器配置的projectId
的数据。
RulesTestContext.firestore(settings?: Firestore.FirestoreSettings) => Firestore;
此方法为此测试上下文获取一个 Firestore 实例。返回的 Firebase JS 客户端 SDK 实例可以与客户端 SDK API(v9 模块化或 v9 兼容)一起使用。
实时数据库
实时数据库
RulesTestEnvironment.clearDatabase() => Promise<void>
此方法会清除实时数据库中属于为实时数据库模拟器配置的projectId
的数据。
RulesTestContext.database(databaseURL?: Firestore.FirestoreSettings) => Firestore;
获取此测试上下文的实时数据库实例。返回的 Firebase JS 客户端 SDK 实例可以与客户端 SDK API(v9 模块化或 v9 兼容)一起使用。该方法接受实时数据库实例的 URL。如果指定,则返回具有从 URL 中提取的参数的命名空间模拟版本的实例。
云储存
云储存
RulesTestEnvironment.clearStorage() => Promise<void>
此方法清除属于为 Cloud Storage 模拟器配置的projectId
的存储桶中的对象和元数据。
RulesTestContext.storage(bucketUrl?: string) => Firebase Storage;
此方法返回一个配置为连接到模拟器的存储实例。该方法接受 Firebase 存储桶的gs://
url 以进行测试。如果指定,则返回存储桶名称模拟版本的存储实例。
使用 v8 JavaScript SDK 运行本地单元测试
选择一个产品以查看 Firebase Test SDK 用于与模拟器交互的方法。
云端 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
此方法返回一个承诺,如果输入成功则拒绝该承诺,如果输入被拒绝则该承诺成功。如果数据库读取或写入失败,请使用它来断言。
firebase.assertFails(app.firestore().collection("private").doc("super-secret-document").get());
assertSucceeds(pr: Promise) => Promise
此方法返回一个承诺,如果输入成功则成功,如果输入被拒绝则被拒绝。使用它来断言数据库读取或写入是否成功。
firebase.assertSucceeds(app.firestore().collection("public").doc("test-document").get());
clearFirestoreData({ projectId: string }) => Promise
此方法会清除与本地运行的 Firestore 实例中特定项目关联的所有数据。使用此方法在测试后进行清理。
firebase.clearFirestoreData({ projectId: "my-test-project" });
实时数据库
实时数据库
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
使用它来设置数据库的规则。
将规则发送到本地运行的数据库。采用一个选项对象,该对象将您的“数据库名称”和“规则”指定为字符串。
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
返回一个承诺,如果输入成功则拒绝,如果输入被拒绝则返回成功。
使用它来断言数据库读取或写入失败:
firebase.assertFails(app.database().ref("secret").once("value"));
assertSucceeds(pr: Promise) => Promise
返回一个承诺,如果输入成功则成功,如果输入被拒绝则被拒绝。
使用它来断言数据库读取或写入成功:
firebase.assertSucceeds(app.database().ref("public").once("value"));
云储存
云储存
initializeTestApp({ storageBucket: string, auth: Object }) => FirebaseApp
使用它来创建一个应用程序作为特定用户进行身份验证以在测试中使用。
返回与选项中指定的存储桶名称和 auth 变量覆盖相对应的初始化 firebase 应用程序。
firebase.initializeTestApp({
storageBucket: "my-bucket",
auth: { uid: "alice" }
});
initializeAdminApp({ storageBucket: string }) => FirebaseApp
使用它来创建一个以管理员身份验证的应用程序,以设置测试状态。
返回与选项中指定的存储桶名称相对应的已初始化管理 firebase 应用程序。此应用程序在读取和写入存储桶时会绕过安全规则。
firebase.initializeAdminApp({ storageBucket: "my-bucket" });
loadStorageRules({ storageBucket: string, rules: Object }) => Promise
使用它来设置存储桶的规则。
将规则发送到本地管理的存储桶。采用一个选项对象,该对象将您的“storageBucket”和“rules”指定为字符串。
firebase
.loadStorageRules({
storageBucket: "my-bucket",
rules: fs.readFileSync("/path/to/storage.rules", "utf8")
});
apps() => [FirebaseApp]
返回所有当前初始化的测试和管理应用程序。
使用它在测试之间或之后清理应用程序(请注意,具有活动侦听器的初始化应用程序会阻止 JavaScript 退出):
Promise.all(firebase.apps().map(app => app.delete()))
assertFails(pr: Promise) => Promise
返回一个承诺,如果输入成功则拒绝,如果输入被拒绝则返回成功。
使用它来断言存储桶读取或写入失败:
firebase.assertFails(app.storage().ref("letters/private.doc").getMetadata());
assertSucceeds(pr: Promise) => Promise
返回一个承诺,如果输入成功则成功,如果输入被拒绝则被拒绝。
使用它来断言存储桶读取或写入成功:
firebase.assertFails(app.storage().ref("images/cat.png").getMetadata());
用于 JS SDK v8 的 RUT 库 API
选择一个产品以查看 Firebase Test SDK 用于与模拟器交互的方法。
云端 Firestore
云端 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
此方法返回一个承诺,如果输入成功则拒绝该承诺,如果输入被拒绝则该承诺成功。如果数据库读取或写入失败,请使用它来断言。
firebase.assertFails(app.firestore().collection("private").doc("super-secret-document").get());
assertSucceeds(pr: Promise) => Promise
此方法返回一个承诺,如果输入成功则成功,如果输入被拒绝则被拒绝。使用它来断言数据库读取或写入是否成功。
firebase.assertSucceeds(app.firestore().collection("public").doc("test-document").get());
clearFirestoreData({ projectId: string }) => Promise
此方法会清除与本地运行的 Firestore 实例中特定项目关联的所有数据。使用此方法在测试后进行清理。
firebase.clearFirestoreData({ projectId: "my-test-project" });
实时数据库
实时数据库
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
使用它来设置数据库的规则。
将规则发送到本地运行的数据库。采用一个选项对象,该对象将您的“数据库名称”和“规则”指定为字符串。
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
返回一个承诺,如果输入成功则拒绝,如果输入被拒绝则返回成功。
使用它来断言数据库读取或写入失败:
firebase.assertFails(app.database().ref("secret").once("value"));
assertSucceeds(pr: Promise) => Promise
返回一个承诺,如果输入成功则成功,如果输入被拒绝则被拒绝。
使用它来断言数据库读取或写入成功:
firebase.assertSucceeds(app.database().ref("public").once("value"));
云储存
云储存
initializeTestApp({ storageBucket: string, auth: Object }) => FirebaseApp
使用它来创建一个应用程序作为特定用户进行身份验证以在测试中使用。
返回与选项中指定的存储桶名称和 auth 变量覆盖相对应的初始化 firebase 应用程序。
firebase.initializeTestApp({
storageBucket: "my-bucket",
auth: { uid: "alice" }
});
initializeAdminApp({ storageBucket: string }) => FirebaseApp
使用它来创建一个以管理员身份验证的应用程序,以设置测试状态。
返回与选项中指定的存储桶名称相对应的已初始化管理 firebase 应用程序。此应用程序在读取和写入存储桶时会绕过安全规则。
firebase.initializeAdminApp({ storageBucket: "my-bucket" });
loadStorageRules({ storageBucket: string, rules: Object }) => Promise
使用它来设置存储桶的规则。
将规则发送到本地管理的存储桶。采用一个选项对象,该对象将您的“storageBucket”和“rules”指定为字符串。
firebase
.loadStorageRules({
storageBucket: "my-bucket",
rules: fs.readFileSync("/path/to/storage.rules", "utf8")
});
apps() => [FirebaseApp]
返回所有当前初始化的测试和管理应用程序。
使用它在测试之间或之后清理应用程序(请注意,具有活动侦听器的初始化应用程序会阻止 JavaScript 退出):
Promise.all(firebase.apps().map(app => app.delete()))
assertFails(pr: Promise) => Promise
返回一个承诺,如果输入成功则拒绝,如果输入被拒绝则返回成功。
使用它来断言存储桶读取或写入失败:
firebase.assertFails(app.storage().ref("letters/private.doc").getMetadata());
assertSucceeds(pr: Promise) => Promise
返回一个承诺,如果输入成功则成功,如果输入被拒绝则被拒绝。
使用它来断言存储桶读取或写入成功:
firebase.assertFails(app.storage().ref("images/cat.png").getMetadata());