Cannot mock admin.firestore() during unit tests - javascript

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
Results in firestore is not a function:
Object.defineProperty(admin, "firestore", {
get: function () {
return 32;
}
});
Does not mock firestore() at all and calls the original function which fails hard:
sinon.stub(admin, 'firestore').returns({get() { }});
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.

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.

Related

How can I stub an anonymous function in Sinon?

The following:
const sinon = require('sinon')
const a = () => { return 1 }
sinon.stub(a)
throws TypeError: Attempted to wrap undefined property undefined as function.
stub works if there is an object, so I tried using this. In node.js REPL (v6.11):
> const a = () => { return 1 }
undefined
> this.a
[Function: a]
However, in my mocha spec, it fails:
const a = () => { return 1 }
console.log(a)
// => [Function: a]
console.log(this.a)
// => undefined
What am I missing? How can I make this work?
BTW: I'm aware that I can stub a method of an object, like this: const stub = sinon.stub(object, 'a'), but that's not what I'm after here with this question.
You can't make it work like this. For stubbing, Sinon requires a "root object" because it needs to replace the function reference that you want to stub in that root object. The this in the REPL only works because of how the REPL is implemented. In latest node (v8), it no longer automatically binds functions to this like described.
sinon.stub takes in an object and then you can stub the properties. So you should be able to do
const obj = {
a: (() => return 1; })
};
and then be able to call
const stub = sinon.stub(obj, "a");
As you witnessed, you set const a to be a function in your example -- it needs to be an object and then sinon can stub a specific property in that object. I believe the reason for this is it then gives it something sinon can reference hence why sinon can also support things like object.method.restore().
Another workaround is to bind to this on your own (although that's not recommended):
const a = () => { return 1 }
this.a = a;
sinon.stub(this, 'a').returns(2);
console.log(this.a());
// => 2

Function inside object is not a function

this is my object
var Controller = (function () {
var self = this;
self.get = function (req, res) {
res.send({
success: 'you got me'
});
}
return self;
})()
module.exports = Controller;
and below is how i am trying to pass parameters to the get function from another document.
var Controller = require('../Controller.js');
Controller.get(arg1, arg2);
However nodejs throws 'TypeError: Controller.get is not a function', what am i doing wrong here? Thanks
There are several issues with that code, but it will not cause the TypeError: Controller.get is not a function you describe.
The way you're calling your anonymous function that creates Controller means that this within it will be the global object (loose mode) or undefined (strict mode). Let's assume loose mode as you haven't said you're getting an error assigning get to undefined. That means you're creating a global function called get. It also means that Controller returns the global object.
Neither of those is a good thing. :-)
If you want to export an object with a get function, you don't need to do anything nearly so complicated:
var Controller = {
get: function (req, res) {
res.send({
success: 'you got me'
});
}
};
module.exports = Controller;
Or perhaps
function get() {
res.send({
success: 'you got me'
});
}
module.exports = { get: get };
Since this is in the context of a NodeJS module, that doesn't define a global function (modules are called in a private scope).
Or, if you meant Controller to be a constructor, then you need to call it via new and reorganize it slightly:
function Controller() {
var self = this; // If you need it for something, you don't in your example
self.get = function get() {
res.send({
success: 'you got me'
});
};
}
module.exports = Controller;
then use it via new:
var Controller = require('./.Controller.js');
var c = new Controller();
c.get("foo", "bar");
It's also probably worth pointing out that require('../Controller.js') uses the Controller.js file from the parent directory, not the current directory. Just in case that wasn't on purpose and you're getting the TypeError: Controller.get is not a function because you're getting the wrong file.

using closure to make private properties javascript

I have this prompt on code wars
"There's no such thing as private properties on a javascript object! But, maybe there are?
Implement a function createSecretHolder(secret) which accepts any value as secret and returns an object with ONLY two methods"
I'm pretty sure it wants me to use closures to achieve this and I have read about how to do this here:
Private variables and closures
https://developer.mozilla.org/en-US/Add-ons/SDK/Guides/Contributor_s_Guide/Private_Properties
This is my code:
function createSecretHolder(secret) {
return {
var _secret = secret;
this.getSecret = function(){
return _secret;
}
this.setSecret = function(secret){
_secret = secret;
}
}
}
However, I get this error:
[eval]:6
var _secret = secret;
^
SyntaxError: Unexpected token =
at Object. ([eval]-wrapper:6:22)
at
at evalScript (node.js:536:25)
at startup (node.js:80:7)
at node.js:906:3
I tried to make an object literal with a private value to hold the value of secret and mostly followed the examples from the sources I listed above. How do I create a closure with ONLY two methods to get and set data and where do I store the value of secret without adding another property?
You are trying to return an object literal, in which you cannot have an assignment statement. To have the closure property, you need to store the variable in the function scope, like this
function createSecretHolder(secret) {
var _secret = secret;
return {
getSecret: function() {
return _secret;
},
setSecret: function(secret) {
_secret = secret;
}
}
}
Now, _secret is in the scope of the getSecret and setSecret functions, because of the closure property. So they can access it.
The problem is return keyword actually returns object with a structure which you didn't follow.
try this:
function createSecretHolder() {
var _secret = secret;
return {
getSecret : function(){
return _secret;
}
,
setSecret : function(secret){
_secret = secret;
}
}
}
Using _ to prepend a property is a common naming convention JavaScript developers follow to show the intention that the property should not be altered, and it is definitely related to this prompt, but it seems like it is not hinting that it's needed and it may not be required for this challenge specifically.
The SyntaxError you're seeing is related to the _secret initialization being within the object literals rather than outside. Object literal syntax is different from variable declaration & assignment and should be mixed. You can move the var _secret = secret; to outside of and before the return statement (but still inside of the function createSecretHolder). (Super Hornet is right, but his code is missing the secret parameter, so his code would get a Reference error saying that secret is not defined.) While your attempt is slightly more declarative, you can actually do without the const _secret = secret. The first thing that the function does is to pair its argument with its parameter, in this case, declaring secret and assigning the input to it. So it is already "in closures".
Here's my take and it works almost the same, except I like to include the console.log() into my functions that I'm expecting to see a result.
function createSecretHolder(secret) {
// input: secret
// output: object (with only 2 methods)
return {
getSecret() { console.log(secret) },
setSecret(input) { secret = input }
}
}
const obj1 = createSecretHolder(5);
obj1.getSecret() // => 5
obj1.setSecret(2)
obj1.getSecret() // => 2
Same as:
function createSecretHolder2(secret) {
return {
getSecret() { return secret },
setSecret(input) { secret = input }
}
}
const obj2 = createSecretHolder2(91);
console.log(obj2.getSecret()) // => 91
console.log(obj2.setSecret(82)) // => undefined
obj2.setSecret('new Secret') // logs nothing, returns undefined
obj2.getSecret() // logs nothing, returns 'new Secret'

Creating test objects in Sinon.js

I'm trying to test code using Sinon.js, but I'm unfamiliar with out it's supposed to behave.
I expect that I can create a 'fake' object, wrap it with sinon and pass it to whatever I'm testing, and have it do its thing. However, it seems like every time I try to wrap a sinon object, the function is not there:
var event_api = {
startTime: function() {
return '123';
}
}
var stub = sinon.stub(event_api);
console.log(stub.startTime()) // returns undefined
var mock = sinon.mock(event_api);
console.log(mock.startTime()) // returns undefined
What am I missing?
It depends on what are you trying to do:
If you don't have any expectations on the call then you should use a stub, for example startTime() only has to return a value.
var event_api = {
startTime: sinon.stub().returns('123')
}
console.log(event_api.startTime());
But if what you want is to set some assertions for the call, then you should use a mock.
var event_api = {
startTime: function() {
return '123';
}
}
//code to test
function getStartTime(e) {
return e.startTime();
}
var mock = sinon.mock(event_api);
mock.expects("startTime").once();
getStartTime(event_api);
mock.verify();
Hope this helps.
The function is indeed there, but it's void of any functionality, since it has been stubbed.
If you want to log the function itself in the console, you have to execute:
console.log(stub.startTime) //logs the function itself
instead of:
console.log(stub.startTime()) //logs the result of the function, which is undefined
However, as said, all the methods of a stub object have been "emptied" of their functionality. If you want to make a method of a stubbed object return a value, you can do the following:
var stub = sinon.stub(event_api);
stub.startTime.returns(123);
console.log(stub.startTime) //log the function
console.log(stub.startTime()) //log the result of function, that is now 123

module.exports function undefined when passing in a variable

In my Node.js app, I'm passing in variables to functions by using require like so:
console.log(require('../controllers/controller')(variable)); // undefined
However, when I don't pass in the variable, it logs as a function, like so:
console.log(require('../controllers/controller')); // [Function]
My controllers are defined like this:
var Controller = function (variable) {
this.variable = variable;
};
Controller.prototype.method = function (someInput, callback) {
// can access this.variable;
};
module.exports = Controller;
I also get this error:
TypeError: Object function (variable) {
this.variable = variable;
} has no method 'method'
Any idea as to where I'm going wrong here? I'm stuck at this step and not sure how to debug further.
require('../controllers/controller') is a function. When you use it without new keyword it does not return anything. But when you use new function() it acts like a constuctor of the object. So what you want to do is to use new keyword if you need an object to be returned with its prototype methods.
var Controller = require('../controllers/controller'),
controller = new Controller(variable);
this is an old thread, but I had this issue and the accepted answer didn't help me.
To create a module with a parameter, I use this code:
module.exports = function(pName) {
return {
test1: function() {
console.log('In test 1 '+pName);
},
test2: function() {
console.log('In test 2 '+pName);
}
};
};
And to call the module's functions:
var testModule = require('testModule')('David');
testModule.test1();
testModule.test2();

Categories