דף זה מתאר שיטות עבודה מומלצות וכלים לכתיבת מבחני יחידות עבור הפונקציות שלך, כגון מבחנים שיהיו חלק ממערכת אינטגרציה מתמשכת (CI). כדי להקל על הבדיקות, Firebase מספקת את Firebase Test SDK עבור פונקציות ענן. הוא מופץ ב-npm כ- firebase-functions-test
, והוא SDK נלווה לבדיקה ל- firebase-functions
. Firebase Test SDK עבור פונקציות ענן:
- דואג להגדרה ולפירוק המתאימים עבור הבדיקות שלך, כגון הגדרה וביטול של משתני סביבה הדרושים
firebase-functions
. - יוצר נתונים לדוגמה והקשר של אירוע, כך שאתה רק צריך לציין את השדות הרלוונטיים לבדיקה שלך.
הגדרת בדיקה
התקן גם firebase-functions-test
וגם את Mocha , מסגרת בדיקה, על ידי הפעלת הפקודות הבאות בתיקיית הפונקציות שלך:
npm install --save-dev firebase-functions-test
npm install --save-dev mocha
לאחר מכן צור תיקיית test
בתוך תיקיית הפונקציות, צור בתוכה קובץ חדש עבור קוד הבדיקה שלך, ותן לו שם משהו כמו index.test.js
.
לבסוף, שנה את functions/package.json
כדי להוסיף את הדברים הבאים:
"scripts": {
"test": "mocha --reporter spec"
}
לאחר שכתבת את המבחנים, תוכל להפעיל אותם על ידי הפעלת npm test
בתוך ספריית הפונקציות שלך.
אתחול Firebase Test SDK עבור פונקציות ענן
ישנן שתי דרכים להשתמש firebase-functions-test
:
- מצב מקוון (מומלץ): כתוב מבחנים המקיימים אינטראקציה עם פרויקט Firebase המוקדש לבדיקה, כך שכותבת מסד נתונים, משתמש יוצר וכו' יתבצעו בפועל, וקוד הבדיקה שלך יוכל לבדוק את התוצאות. זה גם אומר ש-SDKs אחרים של Google המשמשים בפונקציות שלך יעבדו גם כן.
- מצב לא מקוון: כתוב בדיקות יחידות מוצקות ולא מקוונות ללא תופעות לוואי. משמעות הדבר היא שכל קריאות למתודה המקיימות אינטראקציה עם מוצר Firebase (למשל כתיבה למסד הנתונים או יצירת משתמש) צריכות להיות מנותקות. השימוש במצב לא מקוון בדרך כלל אינו מומלץ אם יש לך פונקציות Cloud Firestore או Realtime Database, מכיוון שהוא מגדיל מאוד את המורכבות של קוד הבדיקה שלך.
אתחול SDK במצב מקוון (מומלץ)
אם ברצונך לכתוב בדיקות המקיימות אינטראקציה עם פרויקט בדיקה, עליך לספק את ערכי תצורת הפרויקט הדרושים לאתחול האפליקציה דרך firebase-admin
, ואת הנתיב לקובץ מפתח של חשבון שירות.
כדי לקבל את ערכי התצורה של פרויקט Firebase שלך:
- פתח את הגדרות הפרויקט שלך במסוף Firebase .
- באפליקציות שלך, בחר את האפליקציה הרצויה.
בחלונית הימנית, בחר באפשרות להוריד קובץ תצורה עבור אפליקציות אפל ואנדרואיד.
עבור אפליקציות אינטרנט, בחר Config כדי להציג ערכי תצורה.
כדי ליצור קובץ מפתח:
- פתח את החלונית חשבונות שירות של מסוף Google Cloud.
- בחר את חשבון השירות המוגדר כברירת מחדל של App Engine, והשתמש בתפריט האפשרויות מימין כדי לבחור צור מפתח .
- כאשר תתבקש, בחר JSON עבור סוג המפתח, ולחץ על צור .
לאחר שמירת קובץ המפתח, אתחל את ה-SDK:
// At the top of test/index.test.js
const test = require('firebase-functions-test')({
databaseURL: 'https://my-project.firebaseio.com',
storageBucket: 'my-project.appspot.com',
projectId: 'my-project',
}, 'path/to/serviceAccountKey.json');
אתחול SDK במצב לא מקוון
אם תרצה לכתוב מבחנים לא מקוונים לחלוטין, אתה יכול לאתחל את ה-SDK ללא פרמטרים כלשהם:
// At the top of test/index.test.js
const test = require('firebase-functions-test')();
לועג לערכי תצורה
אם אתה משתמש functions.config()
בקוד הפונקציות שלך, אתה יכול ללעוג לערכי התצורה. לדוגמה, אם functions/index.js
מכיל את הקוד הבא:
const functions = require('firebase-functions');
const key = functions.config().stripe.key;
אז אתה יכול ללעוג לערך בתוך קובץ הבדיקה שלך כך:
// Mock functions config values
test.mockConfig({ stripe: { key: '23wr42ewr34' }});
ייבוא הפונקציות שלך
כדי לייבא את הפונקציות שלך, השתמש require
כדי לייבא את קובץ הפונקציות הראשי שלך כמודול. הקפד לעשות זאת רק לאחר אתחול של firebase-functions-test
, ולעג לערכי תצורה.
// after firebase-functions-test has been initialized
const myFunctions = require('../index.js'); // relative path to functions code
אם אתחולת את firebase-functions-test
במצב לא מקוון , ויש לך admin.initializeApp()
בקוד הפונקציות שלך, אז אתה צריך להדביק אותו לפני ייבוא הפונקציות שלך:
// If index.js calls admin.initializeApp at the top of the file, // we need to stub it out before requiring index.js. This is because the // functions will be executed as a part of the require process. // Here we stub admin.initializeApp to be a dummy function that doesn't do anything. adminInitStub = sinon.stub(admin, 'initializeApp'); // Now we can require index.js and save the exports inside a namespace called myFunctions. myFunctions = require('../index');
בדיקת פונקציות רקע (שאינן HTTP).
התהליך לבדיקת פונקציות שאינן HTTP כולל את השלבים הבאים:
- עטפו את הפונקציה שתרצו לבדוק בשיטת
test.wrap
- בניית נתוני בדיקה
- הפעל את הפונקציה העטופה עם נתוני הבדיקה שבנית ושדות ההקשר של האירוע שתרצה לציין.
- הצהרות לגבי התנהגות.
ראשית עטפו את הפונקציה שברצונכם לבדוק. נניח שיש לך פונקציה ב- functions/index.js
בשם makeUppercase
, אותה תרצה לבדוק. כתוב את הדברים הבאים ב- functions/test/index.test.js
// "Wrap" the makeUpperCase function from index.js
const myFunctions = require('../index.js');
const wrapped = test.wrap(myFunctions.makeUppercase);
wrapped
היא פונקציה שמפעילה makeUppercase
כאשר היא נקראת. wrapped
לוקח 2 פרמטרים:
- נתונים (חובה): הנתונים שיש לשלוח ל-
makeUppercase
. זה מתאים ישירות לפרמטר הראשון שנשלח למטפל בפונקציות שכתבת.firebase-functions-test
מספק שיטות לבניית נתונים מותאמים אישית או נתונים לדוגמה. - eventContextOptions (אופציונלי): שדות של הקשר האירוע שברצונך לציין. ההקשר של האירוע הוא הפרמטר השני שנשלח למטפל בפונקציות שכתבת. אם אינך כולל פרמטר
eventContextOptions
בעת קריאהwrapped
, עדיין נוצר הקשר אירוע עם שדות הגיוניים. אתה יכול לעקוף חלק מהשדות שנוצרו על ידי ציון אותם כאן. שים לב שעליך לכלול רק את השדות שברצונך לעקוף. כל השדות שלא דרסת נוצרים.
const data = … // See next section for constructing test data
// Invoke the wrapped function without specifying the event context.
wrapped(data);
// Invoke the function, and specify params
wrapped(data, {
params: {
pushId: '234234'
}
});
// Invoke the function, and specify auth and auth Type (for real time database functions only)
wrapped(data, {
auth: {
uid: 'jckS2Q0'
},
authType: 'USER'
});
// Invoke the function, and specify all the fields that can be specified
wrapped(data, {
eventId: 'abc',
timestamp: '2018-03-23T17:27:17.099Z',
params: {
pushId: '234234'
},
auth: {
uid: 'jckS2Q0' // only for real time database functions
},
authType: 'USER' // only for real time database functions
});
בניית נתוני בדיקה
הפרמטר הראשון של פונקציה עטופה הוא נתוני הבדיקה שאפשר להפעיל איתם את הפונקציה הבסיסית. ישנן מספר דרכים לבנות נתוני בדיקה.
שימוש בנתונים מותאמים אישית
firebase-functions-test
יש מספר פונקציות לבניית נתונים הדרושים לבדיקת הפונקציות שלך. לדוגמה, השתמש test.firestore.makeDocumentSnapshot
כדי ליצור Firestore DocumentSnapshot
. הארגומנט הראשון הוא הנתונים, והארגומנט השני הוא נתיב ההתייחסות המלא, וישנו ארגומנט שלישי אופציונלי למאפיינים אחרים של תמונת המצב שניתן לציין.
// Make snapshot
const snap = test.firestore.makeDocumentSnapshot({foo: 'bar'}, 'document/path');
// Call wrapped function with the snapshot
const wrapped = test.wrap(myFunctions.myFirestoreDeleteFunction);
wrapped(snap);
אם אתה בודק פונקציה onUpdate
או onWrite
, תצטרך ליצור שתי תמונות: אחת למצב לפני ואחת למצב אחרי. לאחר מכן, תוכל להשתמש בשיטת makeChange
כדי ליצור אובייקט Change
עם תמונות מצב אלה.
// Make snapshot for state of database beforehand
const beforeSnap = test.firestore.makeDocumentSnapshot({foo: 'bar'}, 'document/path');
// Make snapshot for state of database after the change
const afterSnap = test.firestore.makeDocumentSnapshot({foo: 'faz'}, 'document/path');
const change = test.makeChange(beforeSnap, afterSnap);
// Call wrapped function with the Change object
const wrapped = test.wrap(myFunctions.myFirestoreUpdateFunction);
wrapped(change);
עיין בהפניה ל-API עבור פונקציות דומות עבור כל סוגי הנתונים האחרים.
שימוש בנתונים לדוגמה
אם אינך צריך להתאים אישית את הנתונים המשמשים בבדיקות שלך, firebase-functions-test
מציע שיטות להפקת נתונים לדוגמה עבור כל סוג פונקציה.
// For Firestore onCreate or onDelete functions
const snap = test.firestore.exampleDocumentSnapshot();
// For Firestore onUpdate or onWrite functions
const change = test.firestore.exampleDocumentSnapshotChange();
עיין בהפניה ל-API עבור שיטות לקבלת נתונים לדוגמה עבור כל סוג פונקציה.
שימוש בנתונים סתומים (למצב לא מקוון)
אם אתחולת את ה-SDK במצב לא מקוון, ואתה בודק פונקציה של Cloud Firestore או Realtime Database, עליך להשתמש באובייקט רגיל עם סתימות במקום ליצור DocumentSnapshot
או DataSnapshot
בפועל.
נניח שאתה כותב מבחן יחידה עבור הפונקציה הבאה:
// Listens for new messages added to /messages/:pushId/original and creates an // uppercase version of the message to /messages/:pushId/uppercase exports.makeUppercase = functions.database.ref('/messages/{pushId}/original') .onCreate((snapshot, context) => { // Grab the current value of what was written to the Realtime Database. const original = snapshot.val(); functions.logger.log('Uppercasing', context.params.pushId, original); const uppercase = original.toUpperCase(); // You must return a Promise when performing asynchronous tasks inside a Functions such as // writing to the Firebase Realtime Database. // Setting an "uppercase" sibling in the Realtime Database returns a Promise. return snapshot.ref.parent.child('uppercase').set(uppercase); });
בתוך הפונקציה, נעשה שימוש snap
פעמיים:
-
snap.val()
-
snap.ref.parent.child('uppercase').set(uppercase)
בקוד בדיקה, צור אובייקט רגיל שבו שני נתיבי הקוד הללו יעבדו, והשתמש ב-Sinon כדי לבטל את השיטות.
// The following lines creates a fake snapshot, 'snap', which returns 'input' when snap.val() is called, // and returns true when snap.ref.parent.child('uppercase').set('INPUT') is called. const snap = { val: () => 'input', ref: { parent: { child: childStub, } } }; childStub.withArgs(childParam).returns({ set: setStub }); setStub.withArgs(setParam).returns(true);
משמיעים קביעות
לאחר אתחול ה-SDK, עטיפת הפונקציות ובניית נתונים, אתה יכול להפעיל את הפונקציות הגלומות עם הנתונים שנבנו ולהצהיר על התנהגות. אתה יכול להשתמש בספרייה כגון צ'אי כדי להעלות את הטענות הללו.
הצהרות במצב מקוון
אם אתחולת את Firebase Test SDK for Cloud Functions במצב מקוון , אתה יכול לטעון שהפעולות הרצויות (כגון כתיבת מסד נתונים) בוצעו על ידי שימוש ב- firebase-admin
SDK.
הדוגמה להלן קובעת כי 'INPUT' נכתב במסד הנתונים של פרויקט הבדיקה.
// Create a DataSnapshot with the value 'input' and the reference path 'messages/11111/original'. const snap = test.database.makeDataSnapshot('input', 'messages/11111/original'); // Wrap the makeUppercase function const wrapped = test.wrap(myFunctions.makeUppercase); // Call the wrapped function with the snapshot you constructed. return wrapped(snap).then(() => { // Read the value of the data at messages/11111/uppercase. Because `admin.initializeApp()` is // called in functions/index.js, there's already a Firebase app initialized. Otherwise, add // `admin.initializeApp()` before this line. return admin.database().ref('messages/11111/uppercase').once('value').then((createdSnap) => { // Assert that the value is the uppercased version of our input. assert.equal(createdSnap.val(), 'INPUT'); }); });
ביצוע הצהרות במצב לא מקוון
אתה יכול להצהיר על ערך ההחזר הצפוי של הפונקציה:
const childParam = 'uppercase'; const setParam = 'INPUT'; // Stubs are objects that fake and/or record function calls. // These are excellent for verifying that functions have been called and to validate the // parameters passed to those functions. const childStub = sinon.stub(); const setStub = sinon.stub(); // The following lines creates a fake snapshot, 'snap', which returns 'input' when snap.val() is called, // and returns true when snap.ref.parent.child('uppercase').set('INPUT') is called. const snap = { val: () => 'input', ref: { parent: { child: childStub, } } }; childStub.withArgs(childParam).returns({ set: setStub }); setStub.withArgs(setParam).returns(true); // Wrap the makeUppercase function. const wrapped = test.wrap(myFunctions.makeUppercase); // Since we've stubbed snap.ref.parent.child(childParam).set(setParam) to return true if it was // called with the parameters we expect, we assert that it indeed returned true. return assert.equal(wrapped(snap), true);
אתה יכול גם להשתמש במרגלי סינון כדי לקבוע ששיטות מסוימות נקראו, ועם פרמטרים שאתה מצפה.
בדיקת פונקציות HTTP
כדי לבדוק פונקציות HTTP onCall, השתמש באותה גישה כמו בדיקת פונקציות רקע .
אם אתה בודק פונקציות HTTP onRequest, עליך להשתמש firebase-functions-test
אם:
- אתה משתמש
functions.config()
- הפונקציה שלך מקיימת אינטראקציה עם פרויקט Firebase או ממשקי API אחרים של Google, ואתה רוצה להשתמש בפרויקט Firebase אמיתי ובאישורים שלו עבור הבדיקות שלך.
פונקציית HTTP onRequest לוקחת שני פרמטרים: אובייקט בקשה ואובייקט תגובה. כך תוכל לבדוק את פונקציית הדוגמה addMessage()
:
- תעקוף את פונקציית ההפניה באובייקט התגובה, מכיוון ש-
sendMessage()
קורא לו. - בתוך פונקציית ההפניה מחדש, השתמש ב-chai.assert כדי לעזור בהצהרות לגבי אילו פרמטרים יש לקרוא לפונקציית ההפניה מחדש:
// A fake request object, with req.query.text set to 'input' const req = { query: {text: 'input'} }; // A fake response object, with a stubbed redirect function which asserts that it is called // with parameters 303, 'new_ref'. const res = { redirect: (code, url) => { assert.equal(code, 303); assert.equal(url, 'new_ref'); done(); } }; // Invoke addMessage with our fake request and response objects. This will cause the // assertions in the response object to be evaluated. myFunctions.addMessage(req, res);
ניקוי בדיקה
בסוף קוד הבדיקה שלך, קרא לפונקציית הניקוי. פעולה זו מבטלת משתני סביבה שה-SDK קבע בעת האתחול, ומוחקת יישומי Firebase שייתכן שנוצרו אם השתמשת ב-SDK כדי ליצור מסד נתונים בזמן אמת DataSnapshot
או Firestore DocumentSnapshot
.
test.cleanup();
סקור דוגמאות מלאות ולמד עוד
אתה יכול לעיין בדוגמאות המלאות במאגר Firebase GitHub.
- בדיקת מסד נתונים ופונקציות HTTP בזמן אמת במצב מקוון
- בדיקת מסד נתונים ופונקציות HTTP בזמן אמת במצב לא מקוון
למידע נוסף, עיין בהפניה ל-API עבור firebase-functions-test
.