構建單元測試

Firebase 本機模擬器套件可以更輕鬆地全面驗證您的應用程式的功能和行為。它也是驗證 Firebase 安全規則配置的絕佳工具。使用 Firebase 模擬器在本機環境中執行和自動化單元測試。本文檔中概述的方法應該可以幫助您為應用程式建立和自動化單元測試來驗證您的規則。

如果您還沒有設定 Firebase 模擬器,請設定它

運行模擬器之前

在開始使用模擬器之前,請記住以下幾點:

  • 模擬器最初將載入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規則將失敗)。

與即時資料庫模擬器交互

生產 Firebase 即時資料庫實例可以在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.authenticatedContextRulesTestEnvironment.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 使用者。透過傳回的上下文建立的請求將附加一個模擬身份驗證令牌。 (可選)傳遞定義自訂聲明或覆寫身份驗證令牌有效負載的物件。

在測試中使用傳回的測試上下文物件來存取配置的任何模擬器實例,包括使用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 驗證令牌。

在測試中使用傳回的測試上下文物件來存取配置的任何模擬器實例,包括使用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'), { ... });

仿真器特定方法

另請參閱使用模組化 API 的常見測試方法和實用函數

雲端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(模組化或命名空間、版本 9 或更高版本)一起使用。此方法接受即時資料庫執行個體的 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

如果輸入成功,此方法將傳回一個 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

使用它來創建一個作為特定用戶進行身份驗證的應用程式以在測試中使用。

傳回與選項中指定的資料庫名稱和驗證變數覆蓋相對應的已初始化 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,如果輸入被拒絕則回傳一個 Promise。

使用它來斷言資料庫讀取或寫入成功:

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

雲端儲存

雲端儲存

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

如果輸入成功則傳回一個 Promise,如果輸入被拒絕則回傳成功。

使用此斷言儲存桶讀取或寫入失敗:

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

assertSucceeds(pr: Promise) => Promise

如果輸入成功則傳回一個 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

如果輸入成功,此方法將傳回一個 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

使用它來創建一個作為特定用戶進行身份驗證的應用程式以在測試中使用。

傳回與選項中指定的資料庫名稱和驗證變數覆蓋相對應的已初始化 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,如果輸入被拒絕則回傳一個 Promise。

使用它來斷言資料庫讀取或寫入成功:

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

雲端儲存

雲端儲存

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

如果輸入成功則傳回一個 Promise,如果輸入被拒絕則回傳成功。

使用此斷言儲存桶讀取或寫入失敗:

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

assertSucceeds(pr: Promise) => Promise

如果輸入成功則傳回一個 Promise,如果輸入被拒絕則傳回一個 Promise。

使用它來斷言儲存桶讀取或寫入成功:

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