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

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

  • מטפלת של להגדרה ולפירוק המתאים לבדיקות שלך, כגון משתנים הגדרה וסביבה 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 בתוך ספריית הפונקציות שלך.

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

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

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

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

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

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

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

    לאפליקציות רשת, בחר 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();

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

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

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