14

I am reading how to mock google cloud functions for firebase and have issues of properly mocking the following code:

const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
var db = admin.firestore();

The example in the link uses the following code to mock initializeApp which does work

admin = require('firebase-admin');
adminInitStub = sinon.stub(admin, 'initializeApp');

Now admin.firestore is defined in firebase-namespace.js as following:

Object.defineProperty(FirebaseNamespace.prototype, "firestore", {
    get: function () {
        var ns = this;
        var fn = function (app) {
            return ns.ensureApp(app).firestore();
        };
        return Object.assign(fn, require('@google-cloud/firestore'));
    },
    enumerable: true,
    configurable: true
});

I've tried various things to stub this but I fail

  1. Results in firestore is not a function:

        Object.defineProperty(admin, "firestore", {
            get: function () {
                return 32;
            }
        });
    
  2. Does not mock firestore() at all and calls the original function which fails hard:

        sinon.stub(admin, 'firestore').returns({get() { }});
    
  3. TypeError: Cannot stub non-existent own property get

       firestoreStub = sinon.stub(admin.firestore, 'get').callsFake(function () {return {data:"Foo"}});
    

I lack understanding what admin.firebase() actually is. It does not look like it is a property because AFAI when I mock a getter of a property, I would call admin.firebase and not a function admin.firebase(). But it is also not mockable via a function.

Dan McGrath
  • 41,220
  • 11
  • 99
  • 130
Samuel
  • 6,126
  • 35
  • 70

1 Answers1

22

That really took me too long.

To be able to mock the admin.firebase() the getter function of the property should actually return a function.

My initial assumption was that firebase() is a function, which it was not. Then by looking at the implementation I understood that this is a property with a custom getter. However I tried to return some json data block via the getter.

I initially failed to understand that admin.firestore is indeed a property but I was missing the key on why I have to call the property as a function, which is typically not needed on the property itself.

After getting to the point I understood that the getter of the property actually returned a function and that the admin.firebase() can be read like

var method = admin.firebase; // calling the property getter function
method(); // assuming the getter returned a function object

So for my future self ;) this does the trick:

sinon.stub(admin, 'firestore')
   .get(function() { 
       return function() { 
           return "data";
       }
   });

Originally I was trying to do

sinon.stub(admin, 'firestore').get( function () { return "data"; } ); which failed because the admin.firestore() ultimately yielded in "data"(), which made no sense.

Kyle Hotchkiss
  • 10,754
  • 20
  • 56
  • 82
Samuel
  • 6,126
  • 35
  • 70
  • Thanks for posting this, Samuel. I'm about to figure out how to mock firestore queries too wish me luck – Kyle Hotchkiss Feb 02 '18 at 18:10
  • @KyleHotchkiss Good luck :) . I extracted as much js code to external modules and tested them with mocha and sinon as possible. The remaining things in my scenario were still over 100 lines of mock code of firestore functions because my cloud function would read various documents and then create various documents in a transaction, mocking the entire nested promises with different return values with chai etc was a pain in the nether regions. – Samuel Feb 06 '18 at 08:53
  • I tried to implement your stub, for the following code: `const users = await firestore.collection('users').get();`, like this: `const firestoreStub = sinon.stub(); Object.defineProperty(admin, 'firestore', { get: () => { return { collection: (path) => Promise.resolve({mocka: 'user'}) } } });`, but no help. Any idea how to? – DauleDK Apr 12 '18 at 14:21
  • @DauleDK not really, I suggest making a new post where this can be better be read and link both so others and I can look at it. Also probably relevant: Between now and my post firestore has gone 1.0 and has changed their API, I haven't yet had the time to adopt the new API in my code. – Samuel Apr 12 '18 at 14:28
  • @Samuel good point, I created one here: https://stackoverflow.com/questions/49800384/mocking-admin-firestore-collection-get – DauleDK Apr 12 '18 at 15:25
  • 2
    Be aware that if you do this kind of stubbing, you'll break functionality as: `test.firestore.makeDocumentSnapshot()`. – Martin van Dam Apr 17 '19 at 08:50