Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

測試您的 Cloud Firestore 安全規則

在構建應用程序時,您可能希望鎖定對 Cloud Firestore 數據庫的訪問。但是,在啟動之前,您需要更細緻的 Cloud Firestore 安全規則。隨著雲計算公司的FireStore模擬器,除了原型和測試您的應用程序的一般特徵和行為,你可以編寫單元測試,檢查您的雲計算公司的FireStore安全規則的行為。

快速開始

對於一些基本的測試用例簡單的規則,嘗試一下快速啟動示例

了解 Cloud Firestore 安全規則

實施火力地堡認證雲安全的FireStore規則無服務器身份驗證,授權和數據驗證,當你用手機和網絡客戶端庫。

Cloud Firestore 安全規則包括兩部分:

  1. 一個match數據庫中的語句標識的文件。
  2. 一個allow表達控制訪問這些文檔。

Firebase 身份驗證驗證用戶的憑據,並為基於用戶和基於角色的訪問系統奠定基礎。

在讀取或寫入任何數據之前,會根據您的安全規則評估來自 Cloud Firestore 移動/網絡客戶端庫的每個數據庫請求。如果規則拒絕訪問任何指定的文檔路徑,則整個請求都會失敗。

了解更多關於雲計算公司的FireStore安全規則在開始使用雲計算公司的FireStore安全規則開始

安裝模擬器

要安裝雲公司的FireStore仿真器,使用火力地堡CLI並運行以下命令:

firebase setup:emulators:firestore

運行模擬器

首先在您的工作目錄中初始化 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 安全規則的本地文件的名稱,並將這些規則應用於所有項目。如果你不提供本地文件路徑或使用loadFirestoreRules方法如下所述,該仿真器將所有項目具有開放的規則。
  • 雖然大多數火力地堡的SDK與仿真器的工作直接,只有@firebase/rules-unit-testing庫支持嘲諷auth的安全規則,使單元測試更容易。此外,該庫還支持一些特定於模擬器的功能,例如清除所有數據,如下所列。
  • 模擬器還將接受通過客戶端 SDK 提供的生產 Firebase 身份驗證令牌並相應地評估規則,這允許在集成和手動測試中將您的應用程序直接連接到模擬器。

運行本地單元測試

使用 v9 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.

導入後,實施單元測試包括:

  • 創建和配置RulesTestEnvironment有一個呼叫initializeTestEnvironment
  • 沒有觸發規則,使用便捷方法,它可以暫時繞過它們設置的測試數據, RulesTestEnvironment.withSecurityRulesDisabled
  • 設置測試套件和每個測試前/使用電話挂機後清理測試數據和環境,如RulesTestEnvironment.cleanup()RulesTestEnvironment.clearFirestore()
  • 執行,使用模擬驗證狀態測試用例RulesTestEnvironment.authenticatedContextRulesTestEnvironment.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'), { ... });

模擬器特定的方法

也看到在V9 SDK通用試驗方法和實用功能

RulesTestEnvironment.clearFirestore() => Promise<void>

此方法清除在屬於所述數據庫的FireStore數據projectId配置用於公司的FireStore模擬器。

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

此方法獲取此測試上下文的 Firestore 實例。返回的 Firebase JS 客戶端 SDK 實例可與客戶端 SDK API(v9 模塊化或 v9 兼容)一起使用。

可視化規則評估

Cloud Firestore 模擬器可讓您在 Emulator Suite UI 中可視化客戶端請求,包括 Firebase 安全規則的評估跟踪。

打開的FireStore>請求選項卡以查看每個請求的詳細評估序列。

Firestore 模擬器請求監視器顯示安全規則評估

生成測試報告

運行一組測試後,您可以訪問測試覆蓋率報告,其中顯示瞭如何評估每個安全規則。

要獲取報告,請在模擬器運行時查詢模擬器上公開的端點。對於瀏覽器友好的版本,請使用以下 URL:

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

這將您的規則分解為表達式和子表達式,您可以將鼠標懸停在其上以獲取更多信息,包括評估的數量和返回的值。對於此數據的原始 JSON 版本,請在查詢中包含以下 URL:

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

模擬器和生產之間的差異

  1. 您不必顯式創建 Cloud Firestore 項目。模擬器會自動創建任何被訪問的實例。
  2. Cloud Firestore 模擬器不適用於正常的 Firebase 身份驗證流程。相反,在火力地堡測試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 安全規則是否正確允許/拒絕不同的情況。