【问题标题】:How to mock helper functions in Firebase Cloud Functions?如何在 Firebase Cloud Functions 中模拟辅助函数?
【发布时间】:2021-04-22 02:15:27
【问题描述】:

我目前正在探索 Firebase Cloud Functions 中的单元测试,并遇到了一个用例,该用例似乎很频繁,但官方文档并未涵盖。

在为各种进程和触发器编写 Cloud Functions 时,我们通常会使用单独的辅助函数来帮助我们编写更多可重用的代码。例如:

// index.ts
import * as functions from "firebase-functions";

function add(num1: number, num2: number) {
    return num1 + num2;
}

exports.getSum = functions.https.onCall((data, context) => {
    return {
        result: add(data.num1, data.num2)
    }
})

当我现在想测试getSum 时,我必须将它导入我的测试文件并做出断言。但是,这是我遇到问题的地方:

如何断言add 已在getSum 中调用?或者更好:我如何模拟add

到目前为止,我发现的唯一解决方法是同时导出 add,然后修改代码以调用 this.add 而不仅仅是 add,如下所示:

// index.ts
import * as functions from "firebase-functions";

export function add(num1: number, num2: number) {
    return num1 + num2;
}

exports.getSum = functions.https.onCall((data, context) => {
    return {
        // @ts-ignore
        result: this.add(data.num1, data.num2)
    }
})

但是这个解决方案并不理想,因为我 1. 不能保证 this 不是未定义的(这也是我需要 // @ts-ignore 的原因)和 2. 需要修改原始代码进行测试工作。

有什么方法可以在不修改函数代码的情况下完成这项工作?测试这些辅助函数的任何最佳实践?提前谢谢!

这是我用于解决方法的完整单元测试代码:

// index.unit.test.ts
const testEnvironment = require("firebase-functions-test")(
  {
    databaseURL: "",
        storageBucket: "",
        projectId: "",
    },
    "credentials.json"
  );
  const myFunctions = require("../src/index");


describe("getSum", () => {
    let wrapper: any, group: any;

    beforeAll(() => {
        group = myFunctions.addition
        wrapper = testEnvironment.wrap(group.getSum);
    });

    it("adds two numbers", () => {    
        group.add = jest.fn()
    
        wrapper({ num1: 2, num2: 2})

        expect(group.add).toHaveBeenCalled()
    });
});

【问题讨论】:

    标签: typescript firebase unit-testing jestjs google-cloud-functions


    【解决方案1】:

    您可以使用rewire 包来模拟add 函数,而无需修改您的代码。为简单起见,我将使用offline mode 来测试代码。只是为了演示如何模拟add 函数。

    例如

    index.ts:

    import * as functions from "firebase-functions";
    
    function add(num1: number, num2: number) {
      return num1 + num2;
    }
    
    exports.getSum = functions.https.onCall((data, context) => {
      return {
        result: add(data.num1, data.num2),
      };
    });
    

    index.unit.test.ts

    const testEnvironment = require("firebase-functions-test")();
    const rewire = require("rewire");
    
    describe("getSum", () => {
      it("adds two numbers", () => {
        const mod = rewire("./");
        const mAdd = jest.fn().mockReturnValueOnce(666);
        mod.__set__("add", mAdd);
        const wrapper = testEnvironment.wrap(mod.getSum);
        const actual = wrapper({ num1: 2, num2: 2 });
        console.log(actual);
        expect(actual).toEqual({ result: 666 });
        expect(mAdd).toBeCalledWith(2, 2);
      });
    });
    

    npm 脚本:

    "test:cjs": "ts-node -O '{\"module\":\"commonjs\"}' node_modules/jest/bin/jest.js"
    

    使用覆盖率报告运行单元测试

    npm run test:cjs -- --coverage ./index.unit.test.ts
    

    单元测试结果:

    {"severity":"WARNING","message":"Warning, FIREBASE_CONFIG and GCLOUD_PROJECT environment variables are missing. Initializing firebase-admin will fail"}
     PASS  stackoverflow/65763396/index.unit.test.ts
      getSum
        ✓ adds two numbers (433ms)
    
      console.log
        { result: 666 }
    
          at Object.<anonymous> (stackoverflow/65763396/index.unit.test.ts:11:13)
    
    ----------|---------|----------|---------|---------|-------------------
    File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    ----------|---------|----------|---------|---------|-------------------
    All files |       0 |        0 |       0 |       0 |                   
    ----------|---------|----------|---------|---------|-------------------
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        1.316s, estimated 3s
    

    软件包版本:

    "devDependencies": {
        "@types/jest": "^25.2.1",
        "firebase-functions-test": "^0.2.3",
        "jest": "^25.5.4",
        "rewire": "^5.0.0",
        "ts-jest": "^25.5.1",
        "ts-node": "^9.1.1",
        "typescript": "^3.9.2"
      },
      "dependencies": {
        "firebase": "^7.4.0",
        "firebase-admin": "^9.4.2",
        "firebase-functions": "^3.13.1"
      }
    

    【讨论】:

    • 谢谢!不过,使用 "module": "commonjs" 似乎会破坏 firebase-functions-test 配置。我收到了与您在我的测试中所做的相同的警告,但是测试也失败了,因为我也在我的函数中使用了firebase-admin。关于它为什么会损坏以及如何修复它的任何想法?
    • 编辑:刚刚发现似乎与"module": "commonjs" 无关。但仍然无法说出process.env.GCLOUD_PROJECT is not set.
    • @Aside 我可能正在使用 离线模式 测试代码。见firebase.google.com/docs/functions/unit-testing#offline-mode
    猜你喜欢
    • 2018-09-10
    • 2021-04-30
    • 2019-09-17
    • 2023-04-10
    • 2019-11-23
    • 1970-01-01
    • 1970-01-01
    • 2019-09-27
    • 2018-07-15
    相关资源
    最近更新 更多