如何在 Firebase Cloud Functions 中模拟辅助函数?

Posted

技术标签:

【中文标题】如何在 Firebase Cloud Functions 中模拟辅助函数?【英文标题】:How to mock helper functions in 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()
    );
);

【问题讨论】:

【参考方案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  ***/65763396/index.unit.test.ts
  getSum
    ✓ adds two numbers (433ms)

  console.log
     result: 666 

      at Object.<anonymous> (***/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

以上是关于如何在 Firebase Cloud Functions 中模拟辅助函数?的主要内容,如果未能解决你的问题,请参考以下文章

如何将我的 Firebase 实时数据库转移到 Firebase Cloud Firestore

如何在 Firebase Cloud Functions 中确认 PubSub 消息?

Firebase Cloud Functions 如何确认 Cloud pub/sub

如何从 Cloud Function 调用其他 Cloud Firebase Functions

如何将我的 Firebase 实时数据库转移到 Firebase Cloud Firestore

如何在 PC 上本地测试 Cloud Functions for Firebase