How to test `functions.https.onCall` firebase cloud functions locally?
Solution 1
For locally you must call (after firebase.initializeApp)
firebase.functions().useFunctionsEmulator('http://localhost:5000')
Solution 2
Callables are just HTTPS functions with a specific format. You can test just like a HTTPS function, except you have to write code to deliver it the protocol as defined in the documentation.
Solution 3
There is a simple trick, how you can simplify onCall
-function testing. Just declare the onCall function callback as a local function and test that instead:
export const _myFunction = (data, context) => { // <= call this on your unit tests
// Do something
}
exports.myFunction = functions.https.onCall(_myFunction);
Now you can variate all cases with a normal function with the input you define on your function call.
Solution 4
Although the official Firebase Cloud Function docs have not yet been updated, you can now use firebase-functions-test with onCall
functions.
You can see an example in their repository.
I have managed to test my TypeScript functions using jest, here is a brief example. There are some peculiarities here, like import order, so make sure to read the docs :-)
/* functions/src/test/index.test.js */
/* dependencies: Jest and jest-ts */
const admin = require("firebase-admin");
jest.mock("firebase-admin");
admin.initializeApp = jest.fn(); // stub the init (see docs)
const fft = require("firebase-functions-test")();
import * as funcs from "../index";
// myFunc is an https.onCall function
describe("test myFunc", () => {
// helper function so I can easily test different context/auth scenarios
const getContext = (uid = "test-uid", email_verified = true) => ({
auth: {
uid,
token: {
firebase: {
email_verified
}
}
}
});
const wrapped = fft.wrap(funcs.myFunc);
test("returns data on success", async () => {
const result = await wrapped(null, getContext());
expect(result).toBeTruthy();
});
test("throws when no Auth context", async () => {
await expect(wrapped(null, { auth: null })).rejects.toThrow(
"No authentication context."
);
});
});
Solution 5
you should first check for dev environment and then point your functions to local emulator.
For JS:
//after firebase init
if (window.location.host.includes("localhost") ||
window.location.host.includes("127.0.0.1")
) {
firebase
.app()
.functions() //add location here also if you're mentioning location while invoking function()
.useFunctionsEmulator("http://localhost:5001");
}
or if you don't create instance of firebase then
//after firebase init
if (window.location.host.includes("localhost") ||
window.location.host.includes("127.0.0.1")
) {
firebase
.functions()
.useFunctionsEmulator("http://localhost:5001");
}
or when serving pages from backend (node.js):
//after firebase init
if (process.env.NODE_ENV === 'development') {
firebase.functions().useFunctionsEmulator('http://localhost:5001');
}
czphilip
Updated on June 27, 2022Comments
-
czphilip about 2 years
I can't seem to find the solution for this in the Firebase Documentation.
I want to test my
functions.https.onCall
functions locally. Is it possible using the shell or somehow connect my client (firebase SDK enabled) to the local server?I want to avoid having to deploy every time just to test a change to my
onCall
functions.
My code
Function :
exports.myFunction = functions.https.onCall((data, context) => { // Do something });
Client:
const message = { message: 'Hello.' }; firebase.functions().httpsCallable('myFunction')(message) .then(result => { // Do something // }) .catch(error => { // Error handler // });
-
czphilip over 5 yearsAwesome! I will try this out when I have access to the code. BTW is this a newly added feature?
-
cbdeveloper over 5 yearsThis works! I was about to enter a never ending cycle of deploy-edit-deploy... This should be in the documentation guides. Thanks.
-
Eli Himself almost 5 yearsThis works just make sure you are hitting the right port it may not always be :5000, - thank you!
-
Darari Nur Amali over 4 yearsThis is not working in my case. No method for functions().useFunctionsEmulator. Any help?
-
Petr Pololáník over 4 yearsMy imports: import firebase from 'firebase/app'; import 'firebase/functions' // important
-
alexcs over 3 yearsThanks a lot for this idea. Honestly, I feel the testing ergonomics of Firebase callables is abhorrent... Why would I need to stub / mock so many elements of the Firebase internals just to produce a valid test? I just want to check values in and values out. Your solution has the shortcoming that one can't test for the wrapped result of the Firebase callable, but I'll take it.
-
Ville Venäläinen over 3 yearsTo go further, you can use the Firebase's emulator. I have also been developing ts-mock-firebase -library for these purposes. Currently project have been inactive, mainly because the progress that Google has done with its own emulators.
-
Wesley Barnes over 3 yearsCan I just comment that the onCall in the answer here should be onRequest. Thanks for the trick, it seems to work. What a mission