測試 Cloud Firestore 安全性規則

建構應用程式時,建議您鎖定 Cloud Firestore 資料庫不過,在正式推出應用程式前,您可能會需要 Cloud Firestore 安全性規則。使用 Cloud Firestore 模擬器,除了原型設計 及測試應用程式的一般功能與行為 您可以編寫單元測試來檢查 Cloud Firestore 安全性規則的行為。

快速入門導覽課程

如需使用簡單規則的幾個基本測試案例,請參考快速入門導覽課程範例

瞭解 Cloud Firestore 安全性規則

導入 Firebase 驗證並 適用於無伺服器的 Cloud Firestore 安全性規則 驗證、授權和資料驗證 網路用戶端程式庫

Cloud Firestore 安全性規則包含兩項要素:

  1. match 陳述式,用於識別資料庫中的文件。
  2. 控制這些文件存取權的 allow 運算式。

Firebase 驗證會驗證使用者的並奠定良好基礎 以使用者為基礎的存取系統

Cloud Firestore 行動/網路用戶端程式庫提出的每項資料庫要求 會根據您的安全性規則評估,然後再讀取或寫入任何資料。 如果規則拒絕存取任何指定的文件路徑,系統會在 要求失敗。

如要進一步瞭解 Cloud Firestore 安全性規則,請參閱「開始使用 Cloud Firestore 安全性規則」。

安裝模擬器

如要安裝 Cloud Firestore 模擬器,請使用 Firebase CLI 然後執行下列指令:

firebase setup:emulators:firestore

執行模擬器

請先在工作目錄中初始化 Firebase 專案。這是 使用 Firebase CLI 時,常見的第一步。

firebase init

使用下列指令啟動模擬器。模擬器將會執行 直到終止程序:

firebase emulators:start --only firestore

在多數情況下,您想啟動模擬器、執行測試套件,然後關閉 執行測試後,系統會關閉模擬器。只要使用以下工具即可輕鬆操作: emulators:exec 指令:

firebase emulators:exec --only firestore "./my-test-script.sh"

啟動後,模擬器會嘗試在預設通訊埠 (8080) 執行。你可以 修改模擬器的 "emulators" 區段, firebase.json 檔案:

{
  // ...
  "emulators": {
    "firestore": {
      "port": "YOUR_PORT"
    }
  }
}

執行模擬器之前

開始使用模擬器前,請注意下列事項:

  • 模擬器一開始會載入 firestore.rules 中指定的規則 欄位的值firebase.json。它需要一個 包含 Cloud Firestore 安全性規則的本機檔案,然後將這些規則套用至 Google Cloud 的 Resource Manager 工具 經特別設計,能以程式輔助方式協助您管理專案如未提供本機檔案路徑,或使用 如下所述 loadFirestoreRules 方法,模擬器會把所有內容視為 管理專案
  • 雖然 大部分 Firebase SDK 只能直接和模擬器搭配使用,只有 @firebase/rules-unit-testing 程式庫支援 模擬安全性規則中的 auth 可簡化單元測試。此外, 程式庫支援幾項模擬器專屬功能,例如清除所有資料 說明。
  • 模擬器也會接受你提供的正式版 Firebase 驗證權杖 並透過用戶端 SDK 據此評估規則 即可將您的應用程式直接傳送至模擬器,並進行手動測試。

執行本機單元測試

使用第 9 版 JavaScript SDK 執行本機單元測試

Firebase 發布了同時包含兩個版本的安全性規則單元測試程式庫 9 JavaScript SDK 和第 8 版 SDK。程式庫 API 主要 也不一樣建議您使用更精簡的 v9 測試程式庫 連線至模擬器所需的設定較少,因此可以安全避免意外 控管正式環境資源的方式為了兼顧回溯相容性,我們會持續 可用的 v8 測試程式庫

使用 @firebase/rules-unit-testing 模組與模擬器互動 執行在本機的容器如果發生逾時或 ECONNREFUSED 錯誤,請仔細檢查 實際執行的差異

強烈建議您使用新版 Node.js,以便使用 async/await 標記法。您想要測試的所有行為 涉及非同步函式,而測試模組的設計宗旨是讓 以 Promise 為基礎的程式碼。

v9 Rules 單元測試程式庫一律會感知模擬器, 您會很熟悉生產資源

您可使用 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.

匯入後,實作單元測試涉及:

  • 建立及設定 RulesTestEnvironment,其中包含呼叫 initializeTestEnvironment
  • 輕鬆設定測試資料,但不觸發規則 方法可讓您暫時略過 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 驗證權杖。

在測試中使用傳回的測試情境物件存取任何模擬器 設定的執行個體,包括使用 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'), { ... });

模擬器專用方法

另請參閱「第 9 版 SDK 中的常見測試方法和公用程式函式」。

RulesTestEnvironment.clearFirestore() => Promise<void>

這個方法會清除屬於 為 Firestore 模擬器設定的 projectId

RulesTestContext.firestore(settings?: Firestore.FirestoreSettings) => Firestore;

這個方法會取得此測試結構定義的 Firestore 執行個體。傳回的 Firebase JS 用戶端 SDK 執行個體可與用戶端 SDK API (第 9 版模組) 搭配使用 或 v9 Compat)。

以圖表呈現規則評估

Cloud Firestore 模擬器可讓您以視覺化方式呈現用戶端要求, 模擬器套件 UI,包括 Firebase 安全性規則的評估追蹤。

開啟 Firestore >要求分頁,以查看詳細評估 序列。

顯示安全性規則評估作業的 Firestore 模擬器要求監控項目

產生測試報告

執行一系列測試後 涵蓋範圍報告,其中顯示各項安全性規則的評估方式。

如要取得報告,請在模擬器上查詢已公開的端點, 仍在執行中如果是適用於瀏覽器的版本,請使用下列網址:

http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage.html

這會將規則分為可兌換的運算式和子運算式 滑鼠遊標懸停即可查看更多資訊,包括評估數量和值 。這項資料的原始 JSON 版本包含以下網址 :

http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage

模擬器和正式版的差異

  1. 您不必明確建立 Cloud Firestore 專案。模擬器 會自動建立任何可存取的執行個體。
  2. Cloud Firestore 模擬器不適用於一般 Firebase 驗證流程。 不過,在 Firebase Test SDK 中,我們已在initializeTestApp() rules-unit-testing 程式庫,採用 auth 欄位。已建立的 Firebase 帳號代碼 此方法就會表現出 使用您提供的任何實體如果傳入 null,系統會視為 未經驗證的使用者 (例如 auth != null 規則將失敗)。

排解已知問題

使用 Cloud Firestore 模擬器時,您可能會遇到以下已知的 以負載平衡機制分配流量 即可降低應用程式發生效能問題的風險請按照下方指引排解您遇到的任何異常行為 體驗。這些附註是透過安全性規則單元測試所編寫 但通用做法適用於任何 Firebase SDK

測試行為不一致

如果測試偶爾會通過並失敗,即使未變更 因此您可能需要確認測試的正確順序。 大多數與模擬器的互動皆為非同步性質,因此請仔細檢查所有操作 非同步程式碼的順序正確您可以透過下列任一方法修正序列 鏈結承諾,或任意使用 await 標記法。

請特別留意下列非同步作業:

  • 設定安全性規則,例如 initializeTestEnvironment
  • 讀取及寫入資料,例如 db.collection("users").doc("alice").get()
  • 操作斷言,包括 assertSucceedsassertFails

只有在首次載入模擬器時,測試才會通過

模擬器處於有狀態狀態。它會將寫入的所有資料儲存在記憶體中 每當模擬器關閉時,任何資料都會遺失。如果您同時執行多個 以相同的專案 ID 進行測試,每項測試都能產生符合以下描述的資料: 影響後續的測試您可以使用下列任一方法 可以略過以下行為:

  • 請為每個測試使用不重複的專案 ID。請注意,如果您選擇這麼做 需要在每項測試中呼叫 initializeTestEnvironment。規則 只會自動載入預設的專案 ID。
  • 重新建構測試,讓測試不會與先前寫入的資料互動 (例如每項測試使用不同的集合)。
  • 刪除在測試期間寫入的所有資料。

測試設定非常複雜

設定測試時,可能需要 Cloud Firestore 安全性規則實際上不允許。如果您的規則要設定測試 複雜,請嘗試在設定中使用 RulesTestEnvironment.withSecurityRulesDisabled 因此,讀取和寫入不會觸發 PERMISSION_DENIED 錯誤。

之後,您的測試能以驗證或未驗證的身分執行作業 使用者 (使用 RulesTestEnvironment.authenticatedContextunauthenticatedContext) 。如此一來,您就能驗證 Cloud Firestore 安全性規則是否允許 / 拒絕 不同情況的效果