בזמן שאתם מפתחים את האפליקציה, יכול להיות שתרצו לנעול את הגישה למסד הנתונים של Cloud Firestore. עם זאת, לפני ההשקה, תצטרכו Cloud Firestore Security Rules מפורטות יותר. באמצעות האמולטור של Cloud Firestore, בנוסף ליצירת אב טיפוס ובדיקת התכונות וההתנהגות הכלליות של האפליקציה, אתם יכולים לכתוב בדיקות יחידה שבודקות את ההתנהגות של Cloud Firestore Security Rules.
מדריך למתחילים
כדי לראות כמה מקרי בדיקה בסיסיים עם כללים פשוטים, נסו את הדוגמה למדריך למתחילים.
הבנה של Cloud Firestore Security Rules
כדי לבצע אימות, הרשאה ואימות נתונים בלי שרת (serverless) כשמשתמשים בספריות הלקוח לנייד ולאינטרנט, צריך להטמיע את Firebase Authentication ואת Cloud Firestore Security Rules.
Cloud Firestore Security Rules כוללים שני חלקים:
- הצהרת
matchשמזהה מסמכים במסד הנתונים. - ביטוי
allowששולט בגישה למסמכים האלה.
Firebase Authentication מאמת את פרטי הכניסה של המשתמשים ומספק את הבסיס למערכות גישה שמבוססות על משתמשים ועל תפקידים.
כל בקשה למסד נתונים מספריית לקוח של כללים לנייד או לאינטרנט נבדקת מול כללי האבטחה לפני קריאה או כתיבה של נתונים. אם הכללים מונעים גישה לאחד מנתיבי המסמכים שצוינו, הבקשה כולה נכשלת.
מידע נוסף על Cloud Firestore Security Rules זמין במאמר תחילת העבודה עם Cloud Firestore Security Rules.
התקנת האמולטור
כדי להתקין את האמולטור של Cloud Firestore, משתמשים ב-CLI של Firebase ומריצים את הפקודה הבאה:
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 Security Rules שלכם, ומחיל את הכללים האלה על כל הפרויקטים. אם לא תציינו את נתיב הקובץ המקומי או תשתמשו בשיטהloadFirestoreRulesשמתוארת בהמשך, האמולטור יתייחס לכל הפרויקטים כאילו יש להם כללים פתוחים. - רוב ערכות ה-SDK של Firebase פועלות ישירות עם האמולטורים, אבל רק הספרייה
@firebase/rules-unit-testingתומכת בהדמיה שלauthבכללי האבטחה, מה שמקל מאוד על בדיקות היחידה. בנוסף, הספרייה תומכת בכמה תכונות ספציפיות לאמולטור, כמו ניקוי כל הנתונים, כמפורט בהמשך. - האמולטורים יקבלו גם אסימוני Firebase Auth בסביבת הייצור שסופקו דרך ערכות SDK של הלקוח, ויעריכו את הכללים בהתאם. כך אפשר לחבר את האפליקציה ישירות לאמולטורים בשילוב ובבדיקות ידניות.
הרצת בדיקות יחידה מקומיות
הרצת בדיקות יחידה מקומיות באמצעות JavaScript SDK בגרסה 9
Firebase מפיצה ספריית בדיקות יחידה של כללי אבטחה גם עם גרסה 9 של JavaScript SDK וגם עם גרסה 8 של ה-SDK. ממשקי ה-API של הספרייה שונים באופן משמעותי. אנחנו ממליצים על ספריית הבדיקות v9, שהיא יעילה יותר ודורשת פחות הגדרות כדי להתחבר לאמולטורים, וכך להימנע בבטחה משימוש מקרי במשאבי ייצור. כדי לשמור על תאימות לדורות קודמים, אנחנו ממשיכים להציע את ספריית הבדיקות של גרסה 8.
משתמשים במודול @firebase/rules-unit-testing כדי לקיים אינטראקציה עם האמולטור שפועל באופן מקומי. אם אתם מקבלים שגיאות מסוג ECONNREFUSED או שגיאות של זמן קצוב לתפוגה, ודאו שהאמולטור אכן פועל.
מומלץ מאוד להשתמש בגרסה עדכנית של Node.js כדי שתוכלו להשתמש בסימון async/await. כמעט כל ההתנהגות שתרצו לבדוק כוללת פונקציות אסינכרוניות, ומודול הבדיקה מיועד לעבוד עם קוד שמבוסס על Promise.
ספריית Rules Unit Testing בגרסה 9 תמיד מודעת לאמולטורים, והיא אף פעם לא נוגעת במשאבי הייצור.
אתם מייבאים את הספרייה באמצעות הצהרות ייבוא מודולריות של גרסה 9. לדוגמה:
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. - הגדרת חבילת מקרים לבדיקה ו-hooks לפני/אחרי לכל בדיקה עם קריאות לניקוי נתוני הבדיקה והסביבה, כמו
RulesTestEnvironment.cleanup()אוRulesTestEnvironment.clearFirestore(). - הטמעת מקרי בדיקה שמדמים מצבי אימות באמצעות
RulesTestEnvironment.authenticatedContextו-RulesTestEnvironment.unauthenticatedContext.
שיטות נפוצות ופונקציות בסיסיות
כדאי גם לעיין בשיטות בדיקה ספציפיות לאמולטור ב-SDK בגרסה 9.
initializeTestEnvironment() => RulesTestEnvironment
הפונקציה הזו מאתחלת סביבת בדיקה לבדיקת יחידה של כללים. כדי להגדיר את הבדיקה, קודם צריך להפעיל את הפונקציה הזו. כדי שההרצה תצליח, האמולטורים צריכים לפעול.
הפונקציה מקבלת אובייקט אופציונלי שמגדיר TestEnvironmentConfig, שיכול לכלול מזהה פרויקט והגדרות אישיות של האמולטור.
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. לבקשות שנוצרו באמצעות ההקשר שהוחזר יצורף אסימון אימות מדומה. אפשר גם להעביר אובייקט שמגדיר טענות נכונות (claims) בהתאמה אישית או שינויים של המטען הייעודי (payload) של אסימון האימות.
משתמשים באובייקט של הקשר הבדיקה שהוחזר בבדיקות כדי לגשת לכל מופע של אמולטור שהוגדר, כולל אלה שהוגדרו באמצעות 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().doc('/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()
מריצים פונקציית הגדרה לבדיקה עם הקשר שמתנהג כאילו כללי האבטחה מושבתים.
השיטה הזו מקבלת פונקציית קריאה חוזרת, שמקבלת את ההקשר של עקיפת כללי האבטחה ומחזירה אובייקט 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'), { ... });שיטות ספציפיות לאמולטור
אפשר גם לעיין בשיטות בדיקה נפוצות ובפונקציות בסיסיות ב-SDK בגרסה 9.
RulesTestEnvironment.clearFirestore() => Promise<void>
השיטה הזו מוחקת את הנתונים במסד הנתונים של Firestore ששייכים ל-projectId שהוגדר לאמולטור של Firestore.
RulesTestContext.firestore(settings?: Firestore.FirestoreSettings) => Firestore;
השיטה הזו מקבלת מופע של Firestore להקשר הבדיקה הזה. אפשר להשתמש במופע המוחזר של Firebase JS Client SDK עם ממשקי ה-API של Client SDK (גרסה 9 מודולרית או גרסה 9 תואמת).
הצגה חזותית של הערכות הכללים
האמולטור של Cloud Firestore מאפשר לכם לראות בקשות של לקוחות בממשק המשתמש של Emulator Suite, כולל מעקב אחר הערכות של כללי האבטחה של Firebase.
פותחים את הכרטיסייה Firestore > Requests כדי לראות את רצף ההערכה המפורט של כל בקשה.
יצירת דוחות בדיקה
אחרי שמריצים חבילת בדיקות, אפשר לגשת לדוחות כיסוי הבדיקות שמראים איך כל אחד מכללי האבטחה הוערך.
כדי לקבל את הדוחות, שולחים שאילתה לנקודת קצה (endpoint) חשופה באמולטור בזמן שהוא פועל. כדי לקבל גרסה שמתאימה לדפדפן, משתמשים בכתובת ה-URL הבאה:
http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage.html
הפעולה הזו מחלקת את הכללים לביטויים ולביטויי משנה, ואפשר להעביר מעליהם את העכבר כדי לקבל מידע נוסף, כולל מספר ההערכות והערכים שהוחזרו. כדי לקבל את גרסת ה-JSON הגולמית של הנתונים האלה, צריך לכלול את כתובת ה-URL הבאה בשאילתה:
http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage
ההבדלים בין האמולטור לסביבת הייצור
- אתם לא צריכים ליצור פרויקט Cloud Firestore באופן מפורש. האמולטור יוצר באופן אוטומטי כל מכונה שאליה ניגשים.
- האמולטור של Cloud Firestore לא פועל עם התהליך הרגיל של Firebase Authentication.
במקום זאת, ב-Firebase Test SDK, סיפקנו את ה-method
initializeTestApp()בספרייהrules-unit-testing, שמקבלת שדהauth. ה-handle של Firebase שנוצר באמצעות השיטה הזו יתנהג כאילו הוא אומת בהצלחה כישות שסיפקתם. אם תעבירו את הערךnull, הוא יתנהג כמו משתמש לא מאומת (לדוגמה, כלליauth != nullייכשלו).
פתרון בעיות ידועות
במהלך השימוש באמולטור של Cloud Firestore, יכול להיות שתיתקלו בבעיות הידועות הבאות. כדי לפתור בעיות שקשורות להתנהגות לא תקינה, פועלים לפי ההנחיות הבאות. ההערות האלה נכתבו תוך התחשבות בספריית בדיקות היחידה של כללי האבטחה, אבל הגישות הכלליות רלוונטיות לכל Firebase SDK.
התנהגות הבדיקה לא עקבית
אם הבדיקות שלכם עוברות או נכשלות מדי פעם, גם בלי שביצעתם שינויים בבדיקות עצמן, יכול להיות שאתם צריכים לוודא שהן מסודרות ברצף הנכון.
רוב האינטראקציות עם האמולטור הן אסינכרוניות, לכן חשוב לוודא שכל הקוד האסינכרוני מסודר ברצף הנכון. כדי לתקן את הרצף, אפשר לשרשר הבטחות או להשתמש בסימון await באופן נרחב.
חשוב לבדוק את הפעולות האסינכרוניות הבאות:
- הגדרת כללי אבטחה, למשל באמצעות
initializeTestEnvironment. - קריאה וכתיבה של נתונים, למשל באמצעות
db.collection("users").doc("alice").get(). - הצהרות תפעוליות, כולל
assertSucceedsו-assertFails.
הבדיקות עוברות רק בפעם הראשונה שטוענים את האמולטור
האמולטור הוא בעל מצב (stateful). הוא מאחסן בזיכרון את כל הנתונים שנכתבים בו, כך שכל הנתונים אובדים כשהאמולטור נסגר. אם אתם מריצים כמה בדיקות על אותו מזהה פרויקט, כל בדיקה יכולה להפיק נתונים שעשויים להשפיע על הבדיקות הבאות. כדי לעקוף את ההתנהגות הזו, אפשר להשתמש בכל אחת מהשיטות הבאות:
- השתמשו במזהי פרויקטים ייחודיים לכל בדיקה. שימו לב שאם תבחרו לעשות זאת, תצטרכו לקרוא ל-
initializeTestEnvironmentכחלק מכל בדיקה. הכללים נטענים באופן אוטומטי רק למזהה הפרויקט שמוגדר כברירת מחדל. - צריך לשנות את מבנה הבדיקות כך שהן לא יתקשרו עם נתונים שנכתבו בעבר (לדוגמה, להשתמש באוסף שונה לכל בדיקה).
- למחוק את כל הנתונים שנכתבו במהלך בדיקה.
הגדרת הבדיקה מסובכת מאוד
כשמגדירים את הבדיקה, יכול להיות שתרצו לשנות את הנתונים באופן שCloud Firestore Security Rules לא מאפשרים. אם הכללים שלכם הופכים את הגדרת הבדיקה למורכבת, נסו להשתמש ב-RulesTestEnvironment.withSecurityRulesDisabled בשלבי ההגדרה, כדי שפעולות קריאה וכתיבה לא יגרמו לשגיאות PERMISSION_DENIED.
אחרי זה, הבדיקה יכולה לבצע פעולות כמשתמש מאומת או לא מאומת באמצעות RulesTestEnvironment.authenticatedContext ו-unauthenticatedContext בהתאמה. כך אפשר לוודא ש-Cloud Firestore Security Rules מאשר או דוחה מקרים שונים בצורה נכונה.