Home > database >  cloud function unit test mock new document ID
cloud function unit test mock new document ID

Time:01-01

For Firestore cloud function TypeScript unit tests, I want to mock doc().id, but not doc('path'). How should I do that?

admin.firestore().collection('posts').doc().id // I only want to mock this one

admin.firestore().collection('posts').doc('1')

I tried doing the following in sinon. But it gets stuck in an infinite loop at sinon/proxy-invoke.js:50:47:

const collection = admin.firestore().collection('posts');
sinon.stub(collection. 'doc').callsFake(path => 
   path === undefined ? mock : collection.doc(path)
);
sinon.stub(admin.firestore(), 'collection')
  .callThrough()
  .withArgs('posts')
  .returns(collection)

I also tried the following. But the doc(documentPath: string) method seems to be stubbed out as well:

sinon.stub(collection, 'doc')
  //@ts-ignore
  .withArgs()
  .returns(mock)

I am open to using other mock libraries if there is a workaround.

CodePudding user response:

You get an infinite loop because you are recursively calling the stubbed method:

sinon.stub(collection, 'doc').callsFake(path => 
   path === undefined ? mock : collection.doc(path) // <- this calls the stub again
);

First you need to pluck the original doc method from the object you will stub. You also must use the traditional function syntax so that this is properly passed through to the fake callback (you should also pass it to any other functions you call as appropriate). While this doc function only takes a single path argument, you should get into the habit of using rest parameters to make sure you are handling all the arguments.

// store the raw function (make sure this only happens
// once as you don't want to stash a stubbed function)
const _originalDoc = collection.doc;

sinon.stub(collection, 'doc')
  .callsFake(function (...args) { // important! `function` not arrow function
    // In this function, `this` is an instance of CollectionReference
    return args.length = 0 || args[0] === undefined // don't forget the return
      ? mock.call(this)                // call the mocked function
      : _originalDoc.apply(this, args) // call the original with the original arguments
  });

If your mock also calls collection.doc(), make sure that you call the original function, not the stubbed one (unless intentional).

  • Related