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

8

44

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 //
  });
Evacuee answered 16/6, 2018 at 2:41 Comment(0)
R
63

For locally you must call (after firebase.initializeApp)

firebase.functions().useFunctionsEmulator('http://localhost:5000') 
Rotgut answered 16/1, 2019 at 8:50 Comment(7)
Awesome! I will try this out when I have access to the code. BTW is this a newly added feature?Evacuee
This works! I was about to enter a never ending cycle of deploy-edit-deploy... This should be in the documentation guides. Thanks.Doubleacting
This works just make sure you are hitting the right port it may not always be :5000, - thank you!Impersonalize
This is not working in my case. No method for functions().useFunctionsEmulator. Any help?Limewater
My imports: import firebase from 'firebase/app'; import 'firebase/functions' // importantMesarch
Note that useFunctionsEmulator is deprecated; use useEmulator instead: firebase.functions().useEmulator('127.0.0.1', 5001);Inorganic
This method is now deprecated. firebase.google.com/docs/reference/android/com/google/firebase/… Use useEmulator insteadPasturage
L
8

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.

Letitialetizia answered 18/3, 2019 at 17:16 Comment(3)
Thanks 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.Langford
To 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.Authoritarian
Can I just comment that the onCall in the answer here should be onRequest. Thanks for the trick, it seems to work. What a missionTacnode
D
7

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."
    );
  });
});
Dominus answered 8/2, 2019 at 15:39 Comment(0)
I
6

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.

Ifc answered 16/6, 2018 at 3:51 Comment(0)
P
2

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');
}
Picrate answered 17/12, 2019 at 20:2 Comment(0)
M
1

There's a function attached to the HTTPSCallable function called run that:

"Executes the handler function with the provided data as input. Used for unit testing".

You can simply do this:

test("something", async () =>
{
  const res = await myCallableFunction.run({
    data: {
      // arguments
    },
    auth: { // optional
      uid: "test-user",
      token: { email: "test-email" } as any,
    },
    rawRequest: {} as any,
  });
});
Mucor answered 25/7, 2023 at 17:21 Comment(0)
S
0

if you are using angularfire, add this to you app.module

{
  provide: FirestoreSettingsToken,
  useValue: environment.production
    ? undefined
    : {
        host: "localhost:5002",
        ssl: false
      }
}
Succentor answered 29/12, 2019 at 19:31 Comment(0)
R
0

For anyone coming here on 2023/2024, now you need to use useEmulator function (react native example)

if (__DEV__) {
  firebase.functions().useEmulator("localhost", 5001);
}

const myFunctionCall = functions().httpsCallable("FUNC_NAME");

const result = await myFunctionCall(MY_DATA_TO_BE_SENT);
Rosie answered 31/12, 2023 at 17:42 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.