LoginSignup
1

More than 3 years have passed since last update.

firebase functions unit test emulator firebase-functions-test @firebase/testing typescript

Last updated at Posted at 2020-05-14
  • @firebase/testing : use Emulator to run locally .
  • firebase-functions-test : use wrap to test functions in white-box style to get coverage.
  • jest.mock : let firebase functions in index.ts use @firebase/testing to connect with local Emulator.
  • DO NOT use functions Emulator! pls only use firestore Emulator to run! because we are testing functions in white-box style.
  • sh firebase emulators:start --only firestore
  • sh jest --coverage -i company.test
index.ts
import * as admin from 'firebase-admin';
import * as functions from 'firebase-functions';
import { DocumentSnapshot } from 'firebase-functions/lib/providers/firestore';

admin.initializeApp(functions.config().firebase)

console.log('[functions] Start functions')

export interface Company {
  name: string;
  nameInLowerCase?: string
}

export const createCompany = functions.firestore.document('/companies/{companyId}').onCreate(onCreateCompany);

async function onCreateCompany(snapshot: DocumentSnapshot, context: functions.EventContext) {
  const company = snapshot.data() as Company;
  const changes = {} as Company;
  const promises = [] as Promise<any>[];

  // Add createdAt timestamp
  //changes.createdAt = admin.firestore.Timestamp.fromDate(new Date(context.timestamp));

  // add lowercase name
  changes.nameInLowerCase = company.name.toLowerCase();

  // Update only changed properties
  promises.push(snapshot.ref.update(changes));

  // Increase companies count to aggregated company counts document
  promises.push(
    admin
      .firestore()
      .collection('counts')
      .doc('companies')
      .set({ totalCount: admin.firestore.FieldValue.increment(1) }, { merge: true })
  );

  // return promises array so that promises are resolved in parallel.
  return Promise.all(promises);
}
company.test.ts

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

const projectId = 'fir-test-d044e';
// initialize test database
process.env.GCLOUD_PROJECT = projectId;
process.env.FIRESTORE_EMULATOR_HOST = 'localhost:8080';
const db = firebaseEmulator.initializeAdminApp({
  projectId
}).firestore();

jest.mock('firebase-admin', ()=>{
  return {
    initialize: ()=>{},
    firebase: ()=>{
      return db;
    }
  }
});

// create testenv for mocking changes
const testEnv = require('firebase-functions-test')();

import { Company, createCompany } from '../../functions/src/index';

let wrapped: any;

// Create documentReference with created test database
const companyRef = db.collection('companies').doc('companyId1');

describe('Sample tests', () => {
  beforeAll(() => {
    // Creates wrapped test function from cloud function which can be called in tests
    wrapped = testEnv.wrap(createCompany);
  });

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


  test('it should add lowercase name', async () => {
    const data = { name: 'Testers Inc.' };

    // write actual document to database
    await companyRef.set(data);

    // get document snapshot
    const afterSnap = await companyRef.get();

    // Execute the function
    await wrapped(afterSnap);

    const companySnapshot = await companyRef.get();
    const companyAfterCreate = companySnapshot.data() as Company;

    // Assert results
    expect(companyAfterCreate.nameInLowerCase).toBe('testers inc.');
  });

});

other

maybe https://www.npmjs.com/package/firebase-mock could be better.

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1