Search code examples
javascriptunit-testinggoogle-cloud-firestoresinonfirebase-admin

Cannot mock admin.firestore() during unit tests


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.


Solution

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