בדיקת יחידות של פונקציות ענן

דף זה מתאר שיטות עבודה וכלים מומלצים לכתיבת בדיקות יחידה לתפקודים שלך, כגון בדיקות שיהוו חלק ממערכת אינטגרציה רציפה (CI). כדי להקל על הבדיקה, Firebase מספק את SDK Test Firebase עבור פונקציות ענן. זה מופץ על NPM כמו firebase-functions-test , ומהווה SDK מבחן לוויה firebase-functions . SDK לבדיקת Firebase עבור פונקציות ענן:

  • מטפלת של להגדרה ולפירוק המתאים לבדיקות שלך, כגון משתנים הגדרה וסביבה unsetting הדרוש firebase-functions .
  • יוצר נתוני דוגמה והקשר אירוע, כך שתצטרך לציין רק את השדות הרלוונטיים לבדיקה שלך.

הגדרת הבדיקה

התקן הן firebase-functions-test ו מוקה , במסגרת בדיקה, על ידי הרצת הפקודות הבאות בפונקציות שלך התיקייה:

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 בתוך ספריית הפונקציות שלך.

אתחול בדיקת SDK לבדיקת Firebase עבור פונקציות ענן

ישנן שתי דרכים להשתמש firebase-functions-test :

  1. במצב מקוון (מומלץ): בדיקות כתוב כי אינטראקציה עם פרויקט Firebase מוקדשת לבדיקה כך כותב נתון, משתמש יוצר, וכו 'אכן יתרחשו, ואת קוד הבדיקה שלך יכול לבדוק את התוצאות. פירוש הדבר גם שמסמכי SDK אחרים של Google המשמשים בפונקציות שלך יעבדו גם כן.
  2. מצב לא מקוון: כתוב siled ובדיקות יחידות מחוברות ללא תופעות לוואי. המשמעות היא שכל שיחות שיטה שיוצרות אינטראקציה עם מוצר Firebase (למשל כתיבה למסד הנתונים או יצירת משתמש) צריכות להידחק. בדרך כלל לא מומלץ להשתמש במצב אופליין אם יש לך פונקציות Cloud Firestore או מסד נתונים בזמן אמת, מכיוון שהוא מגדיל מאוד את מורכבות קוד הבדיקה שלך.

אתחול SDK במצב מקוון (מומלץ)

אם אתה רוצה בדיקות כתיבה המקיימת אינטראקציה עם פרויקט מבחן, אתה צריך לספק את ערכי config פרויקט דרוש מאתחלים את היישום דרך firebase-admin , והנתיב לקובץ מפתח חשבון שירות.

כדי לקבל את ערכי התצורה של פרוייקט Firebase שלך:

  1. פתח את ההגדרות של הפרויקט שלך קונסולת Firebase .
  2. באפליקציות שלכם, לבחור את האפליקציה הרצויה.
  3. בחלונית הימנית, בחר באפשרות להוריד קובץ תצורה לאפליקציות iOS ו- Android.

    לאפליקציות רשת, בחר Config כדי להציג ערכי תצורה.

ליצירת קובץ מפתח:

  1. פתח את חלונית שירות החשבונות של קונסולת הענן של Google.
  2. בחר חשבון שירות מחדל App Engine, והשתמש בתפריט האפשרויות על הזכות לבחור יצירת מפתח.
  3. כשתתבקש, בחר 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() בקוד הפונקציות שלך, אתה יכול ללעוג ערכי 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 , ולועג ערכי config.

// 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 כולל את השלבים הבאים:

  1. עוטפים את הפונקציה שאתה רוצה לבדוק עם test.wrap השיטה
  2. בנה נתוני בדיקה
  3. הפעל את הפונקציה העטופה עם נתוני הבדיקה שבנית וכל שדות הקשר לאירוע שברצונך לציין.
  4. טען טענות בנוגע להתנהגות.

תחילה עוטף את הפונקציה שברצונך לבדוק. נניח יש לך פונקציה 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 פרמטרים:

  1. נתונים (חובה): הנתונים לשלוח makeUppercase . זה תואם ישירות לפרמטר הראשון שנשלח למטפל הפונקציות שכתבת. firebase-functions-test מספקת שיטות לבניית נתונים מותאמים אישית או למשל.
  2. 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 במצב לא מקוון, בודק פונקצית מסד ענן Firestore או זמן אמת, אתה צריך להשתמש בחפץ רגיל עם גדמים במקום יצירת בפועל 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, עטיפת הפונקציות ובניית נתונים, תוכל להפעיל את הפונקציות העטופות עם הנתונים שנבנו ולהביע טענות על התנהגות. אתה יכול להשתמש כגון ספרייה כמו צ'אי להכנת טענות אלה.

הצהרות במצב מקוון

אם אתה אותחלת ה- SDK מבחן Firebase עבור פונקציות ענן במצב מקוון , אתה יכול לטעון כי הפעולות רצוי (כגון כתיבת מסד) התרחשו באמצעות 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);

אתה גם יכול לשמש מרגלי Sinon לטעון כי שיטות מסוימות נקראו, ועם פרמטרים שאתה מצפה.

בדיקת פונקציות 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);

בדיקת ניקיון

בסוף קוד הבדיקה שלך, התקשר לפונקציית הניקוי. משתני סביבת unsets זה כי סט SDK כשזה אותחל, ומוחק אפליקציות Firebase שעשוי נוצרו אם השתמשת ב- SDK כדי ליצור מסד נתונים בזמן אמת DataSnapshot או Firestore DocumentSnapshot .

test.cleanup();

סקור דוגמאות מלאות ולמד עוד

תוכל לעיין בדוגמאות המלאות במאגר GitHub של Firebase.

כדי ללמוד עוד, עיין הפנית API עבור firebase-functions-test .