Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I wire up the firestore emulator with my firebase functions tests?

Currently we are using 'firebase-functions-test' in online mode to test our firebase functions (as described here https://firebase.google.com/docs/functions/unit-testing), which we setup like so:

//setupTests.ts
import * as admin from 'firebase-admin';

const serviceAccount = require('./../test-service-account.json');

export const testEnv = require('firebase-functions-test')({
    projectId: 'projectId',
    credential: admin.credential.cert(serviceAccount),
    storageBucket: 'projectId.appspot.com'
});
const testConfig = {
    dropbox: {
        token: 'dropboxToken',
        working_dir: 'someFolder'
    }
};

testEnv.mockConfig(testConfig);

// ensure default firebase app exists:
try {
    admin.initializeApp();
} catch (e) {}

We would like to move away from testing against an actual firestore instance in our tests, and use the emulator instead.

The docs, issues, and examples I've been able to find on the net are either outdated, or describe how to set up the emulator for testing security rules, or the web frontend.

Attempts using firebase.initializeAdminApp({ projectId: "my-test-project" }); did not do the trick.

I also tried setting FIRESTORE_EMULATOR_HOST=[::1]:8080,127.0.0.1:8080

So the question is: How can I initialise the firebaseApp in my tests, so that my functions are wired up to the firestore emulator?

like image 362
Bastian Stein Avatar asked Jul 24 '19 21:07

Bastian Stein


Video Answer


1 Answers

I had another crack at it today, more than a year later, so some things have changed, which I can't all list out. Here is what worked for me:

1. Install and run the most recent version of firebase-tools and emulators:

$ npm i -g firebase-tools   // v9.2.0 as of now
$ firebase init emulators

# You will be asked which emulators you want to install. 
# For my purposes, I found the firestore and auth emulators to be sufficient

$ firebase -P <project-id> emulators:start --only firestore,auth

Take note of the ports at which your emulators are available:

console_output

2. Testsetup

The purpose of this file is to serve as a setup for tests which rely on emulators. This is where we let our app know where to find the emulators.

// setupFunctions.ts
import * as admin from 'firebase-admin';

// firebase automatically picks up on these environment variables:
process.env.FIRESTORE_EMULATOR_HOST = 'localhost:8080';
process.env.FIREBASE_AUTH_EMULATOR_HOST = 'localhost:9099';

admin.initializeApp({
    projectId: 'project-id',
    credential: admin.credential.applicationDefault()
});

export const testEnv = require('firebase-functions-test')();

3. Testing a simple function

For this, we setup a simple script which writes a document to firestore. In the test, we assert that the document exists within the emulator, only after we have run the function.

// myFunction.ts
import * as functions from 'firebase-functions';
import {firestore} from 'firebase-admin';

export const myFunction = functions
    .region('europe-west1')
    .runWith({timeoutSeconds: 540, memory: '2GB'})
    .https.onCall(async () => {
        await firestore()
            .collection('myCollection')
            .doc('someDoc')
            .set({hello: 'world'});

        return {result: 'success'};
    });
// myTest.ts
// import testEnv first, to ensure that emulators are wired up
import {testEnv} from './setupFunctions';
import {myFunction} from './myFunction';
import * as admin from 'firebase-admin';

// wrap the function
const testee = testEnv.wrap(myFunction);

describe('myFunction', () => {
    it('should add hello world doc', async () => {
        // ensure doc does not exist before test
        await admin
          .firestore()
          .doc('myCollection/someDoc')
          .delete()

        // run the function under test
        const result = await testee();

        // assertions
        expect(result).toEqual({result: 'success'});

        const doc = await admin
            .firestore()
            .doc('myCollection/someDoc')
            .get();
        expect(doc.data()).toEqual({hello: 'world'});
    });
});

And sure enough, after running the tests, I can observe that the data is present in the firestore emulator. Visit http://localhost:4000/firestore while the emulator is running to get this view.

screenshot of webui showing the created document

like image 80
Bastian Stein Avatar answered Sep 19 '22 13:09

Bastian Stein