Firebase 功能:离线测试时如何存根 firebase-admin 功能

Firebase functions: How to stub firebase-admin functions when testing offline

尝试按照此处的文档来测试 Firease 功能https://firebase.google.com/docs/functions/unit-testing,但不清楚如何使用 sinon 存根 firebase-admin.database。我在下面提供了此错误的函数代码、mocha 测试和控制台输出。从错误输出看来,当函数被调用时 admin.initializeApp(); 正在被调用,但在测试中事先没有被正确存根。

函数代码:

const functions = require('firebase-functions');

// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp();   

// add new users to the database
exports.account = functions.auth.user().onCreate(event => {
  // console.log('addAccount()', event);
  const { uid, providerData } = event;
  const email = providerData[0].email;

  return admin
    .database()
    .ref('/users/' + uid)
    .set({ email, uid });
});

测试重现问题:

const sinon = require('sinon');
const chai = require('chai');
const assert = chai.assert;
const admin = require('firebase-admin');
const test = require('firebase-functions-test')();

describe('Cloud Functions', () => {
  let myFunctions, adminInitStub, adminDatabaseStub;

  before(() => {
    adminInitStub = sinon.stub(admin, 'initializeApp');
    adminDatabaseStub = sinon.stub(admin, 'database');
    myFunctions = require('../index');
  });

  after(() => {
    adminInitStub.restore();
    adminDatabaseStub.restore();
    test.cleanup();
  });

  describe('account', () => {
    it('should write to /users', () => {
      // given
      const setParam = { email: 'test@test.com', uid: 1234 };

      const setStub = sinon.stub();
      setStub.withArgs(setParam).returns(true);

      const refStub = sinon.stub();
      refStub.withArgs('/users/1234').returns({ set: setStub });

      adminDatabaseStub.returns({ ref: refStub });

      const wrapped = test.wrap(myFunctions.account);

      const event = {
        uid: 1234,
        providerData: [{
          email: 'test@test.com'
        }]
      };

      // when
      const actual = wrapped(event);

      // then
      return assert.equal(actual, true);
    });
  });
});

控制台出错

Cloud Functions
    account
      1) should write to /users


  0 passing (53ms)
  1 failing

  1) Cloud Functions
       account
         should write to /users:
     Error: The default Firebase app does not exist. Make sure you call initializeApp() before using any of the Firebase services.
      at FirebaseAppError.FirebaseError [as constructor] (node_modules/firebase-admin/lib/utils/error.js:42:28)
      at FirebaseAppError.PrefixedFirebaseError [as constructor] (node_modules/firebase-admin/lib/utils/error.js:88:28)
      at new FirebaseAppError (node_modules/firebase-admin/lib/utils/error.js:122:28)
      at FirebaseNamespaceInternals.app (node_modules/firebase-admin/lib/firebase-namespace.js:101:19)
      at FirebaseNamespace.app (node_modules/firebase-admin/lib/firebase-namespace.js:402:30)
      at FirebaseNamespace.ensureApp (node_modules/firebase-admin/lib/firebase-namespace.js:418:24)
      at FirebaseNamespace.fn (node_modules/firebase-admin/lib/firebase-namespace.js:280:30)
      at Function.exports.account.functions.auth.user.onCreate.event [as run] (index.js:65:21)
      at wrapped (node_modules/firebase-functions-test/lib/main.js:68:30)
      at Context.it (test/index.test.js:45:22)

解决方案

adminDatabaseStub.get() 更新为 return 一个 getterFn() 函数,然后 return 更新 ref 存根。谢谢@hiranya-jayathilaka

之前

      adminDatabaseStub.returns({ ref: refStub });

之后

adminDatabaseStub.get(function getterFn() {
        return () => {
          return { ref: refStub };
        };
      });

admin.database 存根必须 return 一个可调用对象才能使 admin.database() 工作。 admin.database 在 SDK 中实际上是一个 属性 getter。有关示例和相关讨论,请参阅 https://github.com/firebase/firebase-admin-node/issues/122#issuecomment-339586082