Firebase Local Emulator Suite 可讓您更輕鬆地全面驗證應用程式的功能和行為。這也是驗證 Firebase Security Rules 設定的絕佳工具。使用 Firebase 模擬器在本機環境中執行及自動化單元測試。本文件所述的方法可協助您為應用程式建構並自動化單元測試,以驗證 Rules。
如果您尚未設定 Firebase 模擬器,請先完成這項作業。
執行模擬器前
開始使用模擬器前,請注意下列事項:
- 模擬器一開始會載入
firebase.json
檔案的firestore.rules
或「storage.rules」欄位中指定的規則。如果檔案不存在,且您未使用loadFirestoreRules
或下方所述的「loadStorageRules」方法,模擬器會將所有專案視為具有開放規則。 - 雖然大多數 Firebase SDK 會直接與模擬器搭配運作,但只有
@firebase/rules-unit-testing
程式庫支援模擬安全性規則中的auth
,因此單元測試會變得更加簡單。此外,此程式庫支援一些模擬器專屬功能,例如清除所有資料,如下所列。 - 模擬器也會接受透過用戶端 SDK 提供的正式版 Firebase 驗證權杖,並據此評估規則,讓您在整合和手動測試時,直接將應用程式連結至模擬器。
資料庫模擬器與正式環境的差異
- 您不必明確建立資料庫例項。模擬器會自動建立任何可存取的資料庫例項。
- 每個新資料庫都會啟用封閉規則,因此非管理員使用者無法讀取或寫入資料。
- 每個模擬資料庫都會套用 Spark 方案限制和配額 (最值得注意的是,這會將每個執行個體限制為 100 個並行連線)。
- 任何資料庫都會接受字串
"owner"
做為管理員授權權杖。 - 模擬器目前無法與其他 Firebase 產品互動。請注意,一般 Firebase 驗證流程無法運作。您可以改用
rules-unit-testing
程式庫中的initializeTestApp()
方法,該方法會使用auth
欄位。使用這個方法建立的 Firebase 物件,會以已成功驗證為您提供的實體的行為運作。如果您傳入null
,系統會將其視為未經驗證的使用者 (例如auth != null
規則會失敗)。
與 Realtime Database 模擬器互動
您可以在 firebaseio.com
的子網域中存取正式版 Firebase Realtime Database 例項,並透過以下方式存取 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
符號。您可能想要測試的幾乎所有行為都涉及非同步函式,而測試模組則是為了與以承諾為基礎的程式碼搭配使用而設計。
v9 規則單元測試程式庫一律會辨識模擬器,絕不會觸及您的正式版資源。
您可以使用 v9 模組匯入陳述式匯入程式庫。例如:
import {
assertFails,
assertSucceeds,
initializeTestEnvironment
} 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
。 - 設定測試資料時,不觸發 Rules,而是使用便利方法暫時略過
RulesTestEnvironment.withSecurityRulesDisabled
。 - 設定測試套件和個別測試前/後鉤子,並透過呼叫清理測試資料和環境,例如
RulesTestEnvironment.cleanup()
或RulesTestEnvironment.clearFirestore()
。 - 使用
RulesTestEnvironment.authenticatedContext
和RulesTestEnvironment.unauthenticatedContext
實作模擬驗證狀態的測試案例。
常用方法和公用程式函式
另請參閱使用模組化 API 的模擬器專屬測試方法。
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
,其行為類似於已驗證的 Authentication 使用者。透過傳回的結構定義建立的要求會附加模擬 Authentication 權杖。您可以選擇傳遞定義自訂宣告或覆寫 Authentication 權杖酬載的物件。
在測試中使用傳回的測試內容物件,存取任何已設定的模擬器執行個體,包括使用 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
,其行為類似未透過 Authentication 登入的用戶端。透過傳回的內容建立的要求不會附加 Firebase 驗證權杖。
在測試中使用傳回的測試內容物件,存取任何已設定的模擬器執行個體,包括使用 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()
在與安全性規則停用時相同的情況下,執行測試設定函式。
這個方法會採用回呼函式,該函式會採用 Security-Rules-bypassing 內容並傳回 Promise。承諾解析 / 拒絕後,系統就會銷毀這個內容。
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'), { ... });
模擬器專用方法
Cloud Firestore
Cloud Firestore
RulesTestEnvironment.clearFirestore() => Promise<void>
這個方法會清除 Firestore 資料庫中屬於為 Firestore 模擬器設定的 projectId
的資料。
RulesTestContext.firestore(settings?: Firestore.FirestoreSettings) => Firestore;
這個方法會為此測試內容取得 Firestore 例項。傳回的 Firebase JS Client SDK 例項可搭配用戶端 SDK API (第 9 版模組化或第 9 版相容性) 使用。
Realtime Database
Realtime Database
RulesTestEnvironment.clearDatabase() => Promise<void>
這個方法會清除 Realtime Database 中屬於為 Realtime Database 模擬器設定的 projectId
資料。
RulesTestContext.database(databaseURL?: Firestore.FirestoreSettings) => Firestore;
為此測試情境取得 Realtime Database 例項。傳回的 Firebase JS Client SDK 例項可搭配用戶端 SDK API (模組化或命名空間,版本 9 以上) 使用。此方法會接受即時資料庫例項的網址。如果指定,則會傳回命名空間模擬版本的例項,其中包含從網址擷取的參數。
Cloud Storage
Cloud Storage
RulesTestEnvironment.clearStorage() => Promise<void>
這個方法會清除屬於為 Cloud Storage 模擬器設定的 projectId
的儲存值區中的物件和中繼資料。
RulesTestContext.storage(bucketUrl?: string) => Firebase Storage;
這個方法會傳回已設定為連線至模擬器的 Storage 例項。此方法會接受 gs://
至 Firebase Storage Bucket 的網址,以便進行測試。如果指定,則會傳回值區名稱模擬版本的 Storage 例項。
使用 v8 JavaScript SDK 執行本機單元測試
選取產品,查看 Firebase Test SDK 用於與模擬器連結的方法。
Cloud Firestore
initializeTestApp({ projectId: string, auth: Object }) => FirebaseApp
這個方法會傳回已初始化的 Firebase 應用程式,對應至選項中指定的專案 ID 和驗證變數。您可以使用這項功能,建立以特定使用者身分驗證的應用程式,用於測試。
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" });
Realtime Database
Realtime Database
initializeTestApp({ databaseName: string, auth: Object }) => FirebaseApp
您可以使用這項功能,建立以特定使用者身分驗證的應用程式,用於測試。
傳回已初始化的 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
傳回的應許會在輸入成功時遭到拒絕,在輸入遭到拒絕時則會成功。
您可以使用這項方法,斷言資料庫讀取或寫入作業失敗:
firebase.assertFails(app.database().ref("secret").once("value"));
assertSucceeds(pr: Promise) => Promise
傳回的承諾會在輸入內容成功時成功,在輸入內容遭到拒絕時遭到拒絕。
可用於斷言資料庫讀取或寫入作業是否成功:
firebase.assertSucceeds(app.database().ref("public").once("value"));
Cloud Storage
Cloud Storage
initializeTestApp({ storageBucket: string, auth: Object }) => FirebaseApp
您可以使用這項功能,建立以特定使用者身分驗證的應用程式,用於測試。
傳回已初始化的 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 用於與模擬器連結的方法。
Cloud Firestore
Cloud Firestore
initializeTestApp({ projectId: string, auth: Object }) => FirebaseApp
這個方法會傳回已初始化的 Firebase 應用程式,對應至選項中指定的專案 ID 和驗證變數。您可以使用這項功能,建立以特定使用者身分驗證的應用程式,用於測試。
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" });
Realtime Database
Realtime Database
initializeTestApp({ databaseName: string, auth: Object }) => FirebaseApp
您可以使用這項功能,建立以特定使用者身分驗證的應用程式,用於測試。
傳回已初始化的 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
傳回的應許會在輸入成功時遭到拒絕,在輸入遭到拒絕時則會成功。
您可以使用這項方法,斷言資料庫讀取或寫入作業失敗:
firebase.assertFails(app.database().ref("secret").once("value"));
assertSucceeds(pr: Promise) => Promise
傳回的承諾會在輸入內容成功時成功,在輸入內容遭到拒絕時遭到拒絕。
可用於斷言資料庫讀取或寫入作業是否成功:
firebase.assertSucceeds(app.database().ref("public").once("value"));
Cloud Storage
Cloud Storage
initializeTestApp({ storageBucket: string, auth: Object }) => FirebaseApp
您可以使用這項功能,建立以特定使用者身分驗證的應用程式,用於測試。
傳回已初始化的 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());