How to test `functions.https.onCall` firebase cloud functions locally?

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 //
  });

For locally you must call (after firebase.initializeApp)

firebase.functions().useFunctionsEmulator('http://localhost:5000') 

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.


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.


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."
    );
  });
});