9

I have the firebase emulators running.

My application can call a firebase cloud function that can create a custom user claim if the user is allowed to (depending on a value in the database)

I'm trying to invoke that function:


import * as firebase from '@firebase/testing';
import * as fs from 'fs';

const app = firebase.initializeTestApp({
  databaseName: 'mydb',
  auth: {
    uid: 'useruid'
  },
});

describe('Firebase Functions', () => {
  beforeEach(async () => {
    await firebase.loadDatabaseRules({
      databaseName: 'mydb',
      rules: fs.readFileSync('database.rules.json', 'utf8'),
    });
  });

  afterAll(async () => {
    await Promise.all(firebase.apps().map((app) => app.delete()));
  });

  const cloudfunction = app.functions().httpsCallable('cloudfunction')

  it('throws error if user is not authenticated', async () => {
    await firebase.assertFail(cloudfunction())
  })
});


This is throwing an error:

Error: Error: Response for preflight has invalid HTTP status code 404

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Simon Tran
  • 1,461
  • 1
  • 11
  • 28

2 Answers2

3

In my opinion, you don't need to call firebase from your unit test, because it is not your business is firebase working correctly or not, you need to test your business logic so that means that you will mock the method which calls to the firebase and test expectable returning values of this method, for example, you can write 2 cases when notification will be sent without error and second if firebase service will return failure, for those 2 cases your code should have appropriate behavior. Testing firebase service will be done by the team working on the firebase

Armen Grigoryan
  • 521
  • 2
  • 9
  • I'm writing integration tests. My other tests already interact with a database mocked with Firebase Emulators, why not functions too? I'm testing the whole end2end flow, for example a user might click on a button in the ui that calls a firebase functions that changes data in the database if the users has the right authorizations. I can unit test each steps, but I would also like to test the whole behavior to make sure that there is no error between each step. – Simon Tran Oct 06 '21 at 21:17
  • Just in any case you can mock firebase method, I mean you can test firebase manually, when you will build you application, but in development process I think it more reasonable to mock and test you codes, – Armen Grigoryan Oct 07 '21 at 04:14
  • Even if you are a firebase developer you should test firebase from the inside of firebase, and from the outside application you should yust mock and look at all cases when this service will return some failures or success, you code should expect all cases – Armen Grigoryan Oct 07 '21 at 04:29
  • Otherwise you want to check does you correctly called to this service or not but for that service has libraries and documentations, it is a non checkable part of the code, in unit tests and in integration tests you can test your local business logic – Armen Grigoryan Oct 07 '21 at 04:32
  • I get your point, but this does not solve the issue. For now, I'll mock my functions, but it doesn't make sense for @firebase/testing to not support calling the emulators for cloud functions, when it works for the database. – Simon Tran Oct 07 '21 at 17:12
  • There are many types of implementation integration tests and unit tests, usually both types can be run in different environments even with Jenkins so if you want to config your firebase to your local firebase test you should do it on all environments where will run this test case, I think firebase test more useful for manual testing to check if your configs are done as it must be if you aren't able to connect to the local firebase test you just need to check your firebase service – Armen Grigoryan Oct 10 '21 at 10:11
  • My business logic includes using the firebase admin sdk, with default credentials, in this case it's not doable by this approach –  Jun 01 '23 at 10:46
0

There exists firebase-functions-test for this. This allows you to wrap your cloud functions and call them directly, as well as create stubs for document updates. When running in the context of the emulator, the firebase-admin SDK still interacts with the emulator. A basic setup might look like the following (assuming typescript):

// functions/src/test/functions.test.ts
import {expect} from "chai";
import "mocha";

import firebaseFunctionTest from "firebase-functions-test";
import * as cf from "../index";

const projectId = "demo-project";
const test = firebaseFunctionTest({
  projectId,
});

after(() => test.cleanup());

describe("cloud functions", () => {
  afterEach(async () => {
    await test.firestore.clearFirestoreData(projectId);
  });

  describe("myFunction", () => {
      const myFunction = test.wrap(cf.myFunction);
      // Create mock data with the admin SDK and test function.
  }
}

In your package.json you can the put a script like.

"scripts": {
  "test:unit": "mocha -r ts-node/register --timeout 5000 --exit src/**/*.test.ts",
  "test": "npm run build && npx firebase -P demo-project emulators:exec 'npm run test:unit'",
  ...
}  

You might also want to have a look at https://github.com/firebase/quickstart-testing/tree/master/unit-test-cloud-functions and https://firebase.google.com/docs/functions/unit-testing.

Jarno
  • 6,243
  • 3
  • 42
  • 57
  • This is for testing a cloud function. I want to test my webapp that calls the function. I cannot `import * as cf from "../index";` since the cloud function is declared in a separate project. – Simon Tran Dec 08 '21 at 00:22
  • Ok I misunderstood that. Can you maybe start the emulator in the separate project and connect the project you want to test against that emulator? – Jarno Dec 08 '21 at 09:14
  • The problem is that you cannot call a cloud function running in an emulator from a unit test using the `@firebase/testing` library. I've resorted to simply mocking my database function in my app's unit test – Simon Tran Dec 08 '21 at 19:43