DEV Community

efi shtain
efi shtain

Posted on • Originally published at Medium on

Firebase Firestore unit testing with jest (and kind of typescript)

It took some time but I think I got it

Photo by Ferenc Almasi on Unsplash

I was working the other day on a firebase project. The project is written using typescript, which is nice. It is basically a set of cloud functions which interacts with a Firestore database, among other things.

I wanted to write unit tests and I wanted the tests to run as fast as possible without any dependencies on a real Firestore instance — so I had to mock the Firestore calls.

Fair enough, should be easy… Well….

I wasted so much time trying to mock the complex structure of Firestore types, using way too many Firestore internal data structures when I decided that I must be wrong. If it is hard to mock — you are doing it wrong.

Let’s say I want to mock an update call on a document.

await collection('myCollection')
            .doc('myDoc')
            .update({ updated:true});

I have to mock three functions - collection, doc and update on the firestore instance i’m using. So if I’ll try to do something like this:

import \* as admin from 'firebase-admin';

let update = jest.fn()
let doc = jest.fn(()=>({update})
jest.spyOn(admin.firestore(), 'collection').mockReturnValue({ doc });

The compiler does not like it, it says:

Argument of type ‘{ doc: any; }’ is not assignable to parameter of type ‘CollectionReference<DocumentData>’.
 Type ‘{ doc: any; }’ is missing the following properties from type ‘CollectionReference<DocumentData>’: id, parent, path, listDocuments, and 16 more.

Problem is, I don’t really need most of the functionality of a Firestore collection object or a document object, so I don’t want to waste time mocking it.

The solution, is pretty straightforward, don’t mock what you don’t need.

Just make the return value an unknown type, and then make it any type. It’s kind of anti typescript, but for mocking external dependencies I think I can live with that.

import \* as admin from 'firebase-admin';

const update = jest.fn();
const doc = jest.fn(() => ({update}));
const collection = jest.spyOn(admin.firestore(), 'collection').mockReturnValue((({ doc } as unknown) as any);

voilà!

I can now expect what ever I want on these mocks:

expect(collection).toHaveBeenCalledWith('myCollection');

expect(doc).toHaveBeenCalledWith('myPost');

expect(update).toHaveBeenCalledWith({updated:true});

Happy mocking!

Discussion (0)