如何为JS开发设置Firebase Firestore和云功能测试套件

时间:2019-05-21 10:30:07

标签: firebase google-cloud-firestore google-cloud-functions

根据以下Firebase团队的google I / O(2019)帖子,新的模拟器可让我们结合使用Firebase /数据库和云功能来完全模拟我们的Firebase服务器代码。这也意味着我们应该能够为此编写测试。

  

我们发布了全新的Cloud Functions模拟器,该模拟器还可以   与Cloud Firestore模拟器进行通信。所以如果你想建造   触发Firestore文档更新并写入的函数   数据返回数据库,您可以编码并测试整个流程   在笔记本电脑上本地(来源:Firebase Blog Entry

我可以找到多个资源来查找/描述每个单独的模拟,但没有全部结合在一起

  1. Unit Testing Cloud Function
  2. Emulate Database writes
  3. Emulate Firestore writes

2 个答案:

答案 0 :(得分:3)

对于那些努力测试Firestore触发器的人,我创建了一个示例存储库,希望可以对其他人有所帮助。

https://github.com/benwinding/example-jest-firestore-triggers

它使用玩笑和本地Firebase模拟器。

答案 1 :(得分:2)

要为云功能设置测试环境,以允许您模拟读/写和设置测试数据,您必须执行以下操作。

前提条件

我现在假设您已经建立了一个Firebase项目,其中包含一个functions文件夹和index.js。测试稍后将放在functions/test文件夹中。如果您没有项目设置,请使用firebase init来设置项目。

安装依赖项

首先添加/安装以下依赖项:mocha@firebase/testingfirebase-functions-testfirebase-functionsfirebase-adminfirebase-tools

初始化并启动模拟器

  1. 初始化数据库firebase setup:emulators:database
  2. 初始化Firestore firebase setup:emulators:firestore
  3. 使用firebase emulators:start启动模拟器

这时,您应该正在运行数据库和Firestore模拟器。现在我们可以对其进行读写。

index.js用于示例云功能

// functions/index.js

const fbInit = require("./ainitFb");
let admin = fbInit.getAdmin();

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

exports.addCrewMemeber = functions.firestore
    .document("characters/{characterId}")
    .onCreate(async (characterSnap, context) => {
        const heartOfGold = admin
            .firestore()
            .collection("spaceShip")
            .doc("Heart-of-Gold");

        const heartData = (await heartOfGold.get()).data();

        await heartOfGold.set({
            crew: [...heartData.crew, context.params.characterId],
            crewCount: heartData.crewCount + 1,
        });

        // Update the characters to be in space
        return characterSnap.ref.update({ inSpace: true });
    });

您会发现我没有直接从admin导入firebase-admin。而是从require("./ainitFb")开始。之所以这样做,是因为我们需要对管理员进行打桩,并将新管理员指向模拟器。

创建一个firebase-admin模块以允许模拟firebase-admin

// functions/aniniFb.js

exports.initializeMockApp = function(mock) {
    admin = mock;
};

exports.initializeApp = function() {
    if (!admin) {
        admin = require("firebase-admin");
        admin.initializeApp();
    }
};

exports.getAdmin = function() {
    return global.admin;
};

exports.admin = (inneradmin => {
    return inneradmin;
})(
    global.admin,
);

mocha测试文件test.js

// functions/test/test.js


const assert = require("assert");
const projectId = "dummy";
const firebase = require("@firebase/testing");
const admin = firebase.initializeAdminApp({ projectId, databaseName: projectId });
const test = require("firebase-functions-test")({ projectId, databaseName: projectId });
const fbInit = require("../ainitFb");
// this will inject the mocked admin
fbInit.initializeMockApp(admin);

// load this only after you ran 'initializeMockApp'
const myFunctions = require("../index.js");

// Create a simple document snapshot
// I did that because currently using test.firestore.makeDocumentSnapshot() is not working
// For some reasons it says the firebase db can't be reached, while it's actually running
function makeDocumentSnapshot(data, ref) {
    return {
        id: ref.id,
        data: () => data,
        ref,
    };
}

beforeEach(async function() {
    // we remove the timeout as the default is 2sec which is often too short to also start the emulators
    this.timeout(0);
    // Clear the database between tests
    await firebase.clearFirestoreData({ projectId });
});

it("Add Crew Members", async function() {
    this.timeout(0);

    const heartOfGold = admin
        .firestore()
        .collection("spaceShip")
        .doc("Heart-of-Gold");

    const trillianRef = admin
        .firestore()
        .collection("characters")
        .doc("Trillian");

    // init crew members of the Heart of Gold
    await heartOfGold.set({
        crew: [],
        crewCount: 0,
    });

    // save the character Trillian to the DB
    const trillianData = { name: "Trillian", inSpace: false };
    await trillianRef.set(trillianData);

    // Add Trillian to the Heart of Gold
    const trillianSnap = makeDocumentSnapshot(trillianData, trillianRef);
    const addCrewMemeber = test.wrap(myFunctions.addCrewMemeber);
    await addCrewMemeber(trillianSnap, { params: { characterId: "Trillian" } });

    // check if the crew size has change
    const heart = await heartOfGold.get();
    const trillian = await trillianRef.get();

    // at this point the Heart of Gold has one crew member and trillian is in space 
    assert.deepStrictEqual(heart.data().crewCount, 1, "Crew Members");
    assert.deepStrictEqual(trillian.data().inSpace, true, "In Space");
});


运行测试

要运行测试,我们可以简单地从mocha开始摩卡(如果已全局安装),或者从yarn mocha(如果通过yarn在本地安装)开始。

如果一切正常,您应该会看到以下输出

  √ Add Crew Members (189ms)

  1 passing (223ms)

在CI系统上运行

要在CI系统上运行相同的代码,需要确保已启动仿真器,然后才能运行测试。只需使用firebase emulators:exec命令即可​​完成。例如firebase emulators:exec "mocha --exit"。请不要忘记--exit,否则摩卡咖啡可能无法完成,并且CI运行程序也不会停止。另外,如果您在运行/启动测试或仿真器时遇到麻烦,则可能是在错误的文件夹中初始化了它们。我使用此yarn firebase emulators:exec --only database,firestore "yarn mocha functions --exit"来解决摩卡外观在错误文件夹中的问题。