Reputation: 847
Such as described here, I'm using local emulator (on-line) to make tests im my cloud functions.
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: [
'[email protected]'
]
},
Message: { /* required */
Body: { /* required */
Html: {
Charset: "UTF-8",
Data: JSON.stringfy(message);
}
},
Subject: {
Charset: 'UTF-8',
Data: title
}
},
Source: '"Origin" <[email protected]>',
ReplyToAddresses: [
'[email protected]'
]
};
// 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');
});
});
I execute it with: firebase emulators:exec "npm run test"
I have 2 problems.
1 - When execute, I receive the error TypeError: res.sendStatus is not a function
. If I comment closeConnection
call in block finally (index.js), this code run perfectly and all tests and "expect" run with success. But, this correct way is mock this method or mock 'res' calls. I tried mock with something like this:
const res = {
sendStatus: (status) => {
},
end: () => {
}
}
const result = await wrapped(req, res);
But, I receive this error:
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)
Problem 2:
I'm not wish receive an e-mail each time that tests executes. How I mock sendMail function?
Upvotes: 2
Views: 802
Reputation: 2904
Something very important to point out is that you are currently trying to use a 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. You would only need to change the heading in your index.js
:
exports.saveAndSendMail = functions.https.onRequest(async (req, res) => {
// function body
});
Now, your first problem can then be solved by correctly mocking the res
object that is passed to the function (inside index.test.js
). When testing HTTP functions, you must not use test.wrap()
when calling the function, nor expect the result as you were doing with const result = await wrapped(req);
This is since Wrap
being only supported for testing onCall
functions. You can see another snippet of how to call an HTTP function for testing in the documentation.
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…
For your second problem, I haven’t used AWS SES before, but it seems this library offers ways to mock the functions so that you won’t have to actually send emails during your tests.
Upvotes: 1