Firebase 本地模拟器中的模拟功能

Mock function in Firebase local emulator

如所述here,我正在使用本地模拟器(在线)对我的云功能进行测试。

Index.js:

    var status = 200;
    exports.saveAndSendMail = functions.https.onCall( async (req, res) => {
    try{
        let jsons = req.body;

        await saveInfirestore(jsons);

        await sendMail("Data saved", jsons);

    } finally {
        closeConnection(res, status);
    }
    async function saveInfirestore(json) {
    //execute business logic and save in firestore (irrelevant for this question)
    }


    function closeConnection (res, status){
        res.sendStatus(status);
        res.end();
    }


    async function sendMail(title, message) {
    try {
       AWS.config.loadFromPath('./config_mail.json');

        // Create sendEmail params 
        var params = {
        Destination: { 
            ToAddresses: [
            'mymail@gmail.com'
            ]
        },
        Message: { /* required */
            Body: { /* required */
        Html: {
                Charset: "UTF-8", 
                Data: JSON.stringfy(message);
                }
            },
            Subject: {
                Charset: 'UTF-8',
                Data: title
            }
            },
        Source: '"Origin" <origin@gmail.com>',
        ReplyToAddresses: [
            'origin@gmail.com'
        ]
        };

        // Create the promise and SES service object
        var sendPromise = new AWS.SES({apiVersion: '2022-17-01'}).sendEmail(params).promise();
    }
    catch(e){
        throw e;
    }
    // Handle promise's fulfilled/rejected states
    sendPromise.then(
    function(data) {
        console.log(data.MessageId);
    }).catch(
        function(err) {
        console.error(err, err.stack);
    });
  }

index.test.js

    const { expect } = require("chai");
    const admin = require("firebase-admin");
    
    const test = require("firebase-functions-test")({
        projectId: process.env.GCLOUD_PROJECT,
    });
    
    const myFunctions = require("../index");
    
    describe("Unit tests", () => {
      after(() => {
        test.cleanup();
      });
    
      it("test if save is correct", async () => {
        const wrapped = test.wrap(myFunctions.saveAndSendMail);
    
          const req = {
            body: [{
              value: 5,
              name: 'mario'
            }]
          };
    
        const result = await wrapped(req);
    
        let snap = await db.collection("collection_data").get();
    
        expect(snap.size).to.eq(1);
        
        snap.forEach(doc => {
    
            let data = doc.data();
    
            expect(data.value).to.eql(5);
            expect(data.name).to.eql('mario');
        });
    
    });

我执行它:firebase emulators:exec "npm 运行 test"

我有 2 个问题。

1 - 执行时,我收到错误 TypeError: res.sendStatus is not a function。如果我最终在块中评论 closeConnection 调用 (index.js),此代码 运行 完美且所有测试和“预期” 运行 成功。但是,这种正确的方法是模拟此方法或模拟 'res' 调用。我试过用这样的东西模拟:

       const res = {
            sendStatus: (status) => {
            },
            end: () => {
            }
          }
    
    const result = await wrapped(req, res);

但是,我收到这个错误:

 Error: Options object {} has invalid key "sendStatus"
  at /home/linuxuser/my-project/firebase/functions/myfolder/node_modules/firebase-functions-test/lib/main.js:99:19
  at Array.forEach (<anonymous>)
  at _checkOptionValidity (node_modules/firebase-functions-test/lib/main.js:97:26)
  at wrapped (node_modules/firebase-functions-test/lib/main.js:57:13)
  at Context.<anonymous> (test/index.test.js:50:26)
  at processImmediate (node:internal/timers:464:21)

问题 2:

我不希望每次执行测试时都收到一封电子邮件。我如何模拟 sendMail 函数?

需要指出的非常重要的一点是,您目前正在尝试使用 Firebase callable function, as shown by the function heading functions.https.onCall(() => {});. Since you want to work with requests and response codes, the correct type of function to use is an HTTP function。您只需要更改 index.js:

中的标题
exports.saveAndSendMail = functions.https.onRequest(async (req, res) => {
    // function body
});

现在,您的第一个问题可以通过正确模拟传递给函数(在 index.test.js 内)的 res object 来解决。测试 HTTP 函数时,您不能在调用函数时使用 test.wrap(),也不能像使用 const result = await wrapped(req); 那样期望结果 这是因为 Wrap 仅支持测试 onCall 职能。您可以在 documentation.

中看到关于如何调用 HTTP 函数进行测试的另一个片段
  it("test if save is correct", async () => {
      const req = {
        body: [{
          value: 5,
          name: 'mario'
        }]
      };

      // mocking the response object that is returned from the function:
      const res = {
        sendStatus: (code) => {
          expect(code).to.eql(200); // asserting that we get 200 back as the response code
        },
        end: () => {

        }
      };
    
    const result = await myFunctions.saveAndSendMail(req, res); // mocking a call to an HTTP function, without test.wrap()
    // rest of the function…

对于你的第二个问题,我之前没有使用过 AWS SES,但似乎 this library 提供了模拟函数的方法,这样你就不必在测试期间实际发送电子邮件。