במהלך פיתוח האפליקציה, מומלץ לנעול את הגישה למסד הנתונים Cloud Firestore. עם זאת, לפני ההשקה, תצטרכו לקבל Cloud Firestore Security Rules מפורט יותר. באמצעות המהדר של Cloud Firestore, בנוסף ליצירת אב טיפוס ובדיקת התכונות וההתנהגות הכלליות של האפליקציה, אפשר לכתוב בדיקות יחידה לבדיקת ההתנהגות של Cloud Firestore Security Rules.
מדריך למתחילים
כדי לקבל כמה תרחישי בדיקה בסיסיים עם כללים פשוטים, אפשר לנסות את הדוגמה למדריך למתחילים.
הסבר על Cloud Firestore Security Rules
כשמשתמשים בספריות הלקוח לנייד ולאינטרנט, אפשר להטמיע את Firebase Authentication ו-Cloud Firestore Security Rules לאימות, להרשאה ולתיקוף נתונים ללא שרת.
Cloud Firestore Security Rules כוללים שני חלקים:
- משפט
match
שמזהה מסמכים במסד הנתונים. - ביטוי
allow
שקובע את הגישה למסמכים האלה.
Firebase Authentication מאמת את פרטי הכניסה של המשתמשים ומספק את הבסיס למערכות גישה מבוססות-משתמשים ומבוססות-תפקידים.
כל בקשה למסד נתונים מספריית לקוח לנייד או לאינטרנט של Cloud Firestore נבדקת מול כללי האבטחה שלכם לפני קריאה או כתיבת של נתונים. אם הכללים מונעים גישה לאחד מנתיבי המסמכים שצוינו, הבקשה כולה נכשלת.
מידע נוסף על 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 בסביבת הייצור שסופקו דרך ערכות ה-SDK של הלקוח, ויעריכו את הכללים בהתאם. כך תוכלו לחבר את האפליקציה ישירות לסימולטורים במהלך שילוב ובמהלך בדיקות ידניות.
הרצת בדיקות יחידה מקומיות
הרצת בדיקות יחידה מקומיות באמצעות ה-SDK של JavaScript בגרסה 9
Firebase מפיץ ספריית בדיקות יחידה של כללי אבטחה עם ערכת ה-SDK של JavaScript בגרסה 9 ועם ערכת ה-SDK בגרסה 8. ממשקי ה-API של הספריות שונים באופן משמעותי. מומלץ להשתמש בספריית הבדיקה בגרסה 9, שהיא יעילה יותר ומחייבת פחות הגדרות כדי להתחבר למהדמנים, וכך אפשר להימנע באופן בטוח משימוש בטעות במשאבי הייצור. מטעמי תאימות לאחור, אנחנו ממשיכים להציע את ספריית הבדיקות של v8.
משתמשים במודול @firebase/rules-unit-testing
כדי ליצור אינטראקציה עם הסימולטור שפועל באופן מקומי. אם מופיעות הודעות זמן קצוב פג או שגיאות מסוג ECONNREFUSED
, צריך לוודא שהאמולטור פועל בפועל.
מומלץ מאוד להשתמש בגרסה עדכנית של Node.js כדי שתוכלו להשתמש בסימון async/await
. כמעט כל ההתנהגות שרוצים לבדוק כוללת פונקציות אסינכרוניות, ומודול הבדיקה מיועד לעבוד עם קוד שמבוסס על Promise.
ספריית בדיקת היחידה של הכללים בגרסה 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
. - הגדרת חבילה של בדיקות וכל בדיקה בנפרד עם קוד לפני/אחרי (hook) עם קריאות לניקוי נתוני הבדיקה והסביבה, כמו
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. לבקשות שנוצרות באמצעות ההקשר המוחזר יצורף טוקן אימות מדומה. אפשר גם להעביר אובייקט שמגדיר הצהרות מותאמות אישית או שינוי של נתוני עומס עבודה (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(), '/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 כדי לראות את רצף ההערכה המפורט של כל בקשה.
יצירת דוחות בדיקה
אחרי שמריצים חבילת בדיקות, אפשר לגשת לדוחות כיסוי בדיקות שבהם מוצגת האופן שבו כל אחד מכללי האבטחה שלכם נבדק.
כדי לקבל את הדוחות, שולחים שאילתה לנקודת קצה חשופה במהלך ההפעלה של הסימולטור. כדי לקבל גרסה שמתאימה לדפדפנים, צריך להשתמש בכתובת ה-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 סיפקנו את השיטה
initializeTestApp()
בספרייהrules-unit-testing
, שמקבלת שדהauth
. הכינוי ב-Firebase שנוצר באמצעות השיטה הזו יתנהג כאילו הוא אומת ככל ישות שתספקו. אם מעבירים את הערךnull
, הוא יתנהג כמשתמש לא מאומת (לדוגמה, כלליauth != null
יכשלו).
פתרון בעיות מוכרות
כשמשתמשים במהדמ של Cloud Firestore, יכול להיות שתתקלו בבעיות הידועות הבאות. כדי לפתור בעיות התנהגות לא סדירה, פועלים לפי ההנחיות הבאות. ההערות האלה נכתבו בהתאם לספרייה של בדיקות היחידה של כללי האבטחה, אבל הגישות הכלליות רלוונטיות לכל ערכת SDK של Firebase.
התנהגות הבדיקה לא עקבית
אם הבדיקות עוברות או נכשלות מדי פעם, גם בלי שינויים בבדיקות עצמן, יכול להיות שצריך לוודא שהן מסודרות בצורה נכונה.
רוב האינטראקציות עם הסימולטור הן אסינכרוניות, לכן חשוב לוודא שכל הקוד האסינכרוני ממוספר כראוי. אפשר לתקן את הסדר על ידי שרשור הבטחות או שימוש נרחב בסימון await
.
כדאי לבדוק במיוחד את הפעולות האסינכרוניות הבאות:
- הגדרת כללי אבטחה, למשל
initializeTestEnvironment
. - קריאה וכתיבה של נתונים, למשל באמצעות
db.collection("users").doc("alice").get()
. - טענות נכוֹנוּת תפעוליות, כולל
assertSucceeds
ו-assertFails
.
הבדיקות עוברות רק בפעם הראשונה שטענת את הסימולטור
לאמולטור יש מצב. הוא שומר את כל הנתונים שנכתבו בו בזיכרון, כך שכל הנתונים הולכים לאיבוד כשהמכונה הווירטואלית מושבתת. אם מריצים כמה בדיקות עם אותו מזהה פרויקט, כל בדיקה יכולה לייצר נתונים שעשויים להשפיע על בדיקות הבאות. אתם יכולים להשתמש באחת מהשיטות הבאות כדי לעקוף את ההתנהגות הזו:
- להשתמש במזהי פרויקטים ייחודיים לכל בדיקה. חשוב לזכור שאם תבחרו לעשות זאת, תצטרכו להפעיל את
initializeTestEnvironment
כחלק מכל בדיקה. הכללים נטענים באופן אוטומטי רק עבור מזהה הפרויקט שמוגדר כברירת מחדל. - לשנות את המבנה של הבדיקות כך שלא יהיו להן אינטראקציות עם נתונים שנכתבו בעבר (לדוגמה, להשתמש בקולקציה שונה לכל בדיקה).
- מחיקה של כל הנתונים שנכתבו במהלך הבדיקה.
הגדרת הבדיקה מורכבת מאוד
כשמגדירים את הבדיקה, יכול להיות שתרצו לשנות את הנתונים באופן שלא מאפשרים Cloud Firestore Security Rules בפועל. אם הכללים שלכם הופכים את הגדרת הבדיקה למסובכת, נסו להשתמש ב-RulesTestEnvironment.withSecurityRulesDisabled
בשלבים של ההגדרה, כדי שהקריאות והכתובות לא יפעילו שגיאות PERMISSION_DENIED
.
לאחר מכן, הבדיקה יכולה לבצע פעולות כמשתמש מאומת או לא מאומת באמצעות RulesTestEnvironment.authenticatedContext
ו-unauthenticatedContext
, בהתאמה. כך תוכלו לוודא שהפונקציה Cloud Firestore Security Rules מאשרת או דוחה בקשות בצורה נכונה.