- @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.
-
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.