How can I stub an anonymous function in Sinon? - javascript

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

Related

JavaScript Nested Functions cross referencing

I have a nested function that needs a return type of the previously declared function to use it as a function argument . I don't know if my structure is correct or can support this .
would be grateful for some advice on how to call it
var myObject = {
funct1 : (function (){..... return funct1; })(),
funct2 : (function (funct1){..... return func2; })(funct1)
};
So the question is how would I call that funct1 argument correctly in the second function
Do I use the myObject.Funct1 or is there another method of calling that object internally ...
Im currently getting a error
Cannot read property 'funct1' of undefined
I don't think there is a way to do this by declaring an object literal since the keys of the object can't be used during the object creation.
You can get the same functionality by doing this, though:
const myObject = (() => {
const func1 = () => 'result of func1';
const func2 = () => func1() + ' and func2';
return { func1, func2 }
})();
console.log(myObject.func2()); // result of func1 and func2

Javascript: typeof says "function" but it can't be called as a function

I'm really puzzled with Javascript this time:
var x = Array.prototype.concat.call;
typeof x; // function
x(); // Uncaught TypeError: x is not a function
What on earth is going on here?
If it helps, I also noticed:
x([1,2],[3,4]) does not work either
toString also thinks it's a function:
Object.prototype.toString.call(x); // "[object Function]"
This also happens with Array.prototype.concat.apply.
When it is forced as an expression it also does not work:
(0, Array.prototype.concat.call)([1,2],[3,4]); // Same TypeError
Tested in Chrome and Node.
The error is misleading. x is a function, but it has lost the referenced function (concat), which throws an error
Running on firefox gives a more descriptive error
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Called_on_incompatible_type
What it's saying is that the call function has nothing its bound to.
In the same way that if you take an object like this:
const a = {
b: 2,
test() {
console.log('hi', this.b);
}
};
const c = a.test;
c();
You will get hi undefined as you've lost the relationship of the function to b.
You can fix this by either doing c.bind(a)() or c.call(a)
The call function behaves similarly. It is going to be the same for every function, and the pseudocode would look something like this:
class Function {
constructor(functionDefinition) {
this.functionDefinition = functionDefinition;
}
call(newThis, ...args) {
// take this.functionDefinition, and call it with `this` and `args`
}
}
Since you are extracting out the call function, it loses the function object it's associated with.
You can fix this by either binding concat to the function, or using call on call :-)
const a = []
const boundFn = a.concat.call.bind(a.concat)
console.log(boundFn([3], [1,2]));
// Or, you can use `call` to pass in the concat function
const callFn = a.concat.call;
console.log(callFn.call(a.concat, [4], [1,2]))

How to test the result of a curried function in Jasmine?

Use Case:
I have a module of functions, each function is unit tested
I have a factory function that creates a stream of these functions that a third party library requires.
I would like to test that this factory function produces the correct stream. Using #cycle/Time, I am able to create the stream and assert on the stream.
I am able to assert that the functions appear on the stream in the correct order.
However, I am unable to assert on any function that is curried. How would one assert on curried functions?
Currently, I have a hack in place to JSON.stringify the functions and assert on their source.
To simplify the problem, I created a simple test suite so we aren't concerned with using #cycle/Time. It appears that curried functions are new instances of the function. Please see the code below.
I was wondering how would I be able to make the failing test pass? In this case I simulate the curried function by using bind. Is this possible?
const a = () => b
const b = () => {}
const c = (arg) => b.bind(null, arg)
const d = () => () => {}
describe("curried function test", function() {
it('should return a reference to b', () => {
expect(a()).toBe(b)
})
// This test fails because b.bind returns a new function.
it('should return a reference to a curried b', () => {
expect(c('foo')).toBe(b)
})
it('should create a new instance everytime', () => {
expect(d()).not.toBe(d())
})
});
I've setup a jsfiddle here.
"This test fails because b.bind returns a new function."
That's because what you get from c is the result from b.bind(null, arg), which isn't the same as b.
Otherwise, b.bind would be modifying b.
As mdn says:
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
(source, emphasis mine)
Basically, c can't return a reference to b.
What you do have, is the resulting function's name:
const b = () => {};
const c = (arg) => b.bind(null, arg);
const e = c("foo");
console.log(e.name);
console.log(e.name === `bound ${b.name}`);
So, you could test that e.name equals "bound " + b.name.

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

How to mock a function inside another function (which I am testing) using sinon?

let's say i have a function
Func a() {
//Do Something
let c = b();
return c;
}
I want to test the function a and mock b() and in the mock want to assign c.
Sinon.Stub(Test,"b").returns("DummyValue");
c should be assigned DummyValue.
How can I do that?
describe("a", () => {
let a = a();
//mock b();
action = execute(a);
expect(action).should.return.("DummyValue");
})
When we have 2 functions in the same file and want to stub one of them and test the other.
For example,:
Test: tests.js
let ComputeSumStub = sinon.stub(OfflineLoader, "ComputeSum");
const ans = function ()
{
return 10;
};
ComputeSumStub.returns(ans);
const actualValue: number = OfflineLoader.sum();
expect(actualValue).to.be.equal(10);
Dev: foo.js
function sum(): number
{
return ComputeSum(8, 9);
}
function ComputeSum(a: number, b: number): number
{
return a + b;
}
We cannot do that, because after compilation the functions are exported with different signatures, with full name and while stubbing we stub the global function but while calling it from within the other function, we call the local function, hence it doesn’t work.
There is a workaround to do that.
foo.js
const factory = {
a,
b,
}
function a() {
return 2;
}
function b() {
return factory.a();
}
module.exports = factory;
test.js
const ser = require('./foo');
const sinon = require('sinon');
const aStub = sinon.stub(ser, 'a').returns('mocked return');
console.log(ser.b());
console.log(aStub.callCount);
Ref: Stubbing method in same file using Sinon
You can stub function only
if you pass it as parameter ans fake it with test doubles library like sinon
or if it is dependency (loaded via import or require). In such case you can use proxyquire to pass in your fake b function for module under test. Function itself can be faked by sinon or other test doubles library.
In this case a sinon stub is more appropriate then a mock
When to use mocks vs stubs?
The rule of thumb is: if you wouldn’t add an assertion for some
specific call, don’t mock it. Use a stub instead.
Our assertion in the test is not on a specific call of function a i.e 1st or 3rd call but on all calls.
We can tel this because we have programmed our stub to always return the same result regardless of the way in which it is being called (arguments, or number of calls).
Pass a sinon stub function as an argument to function a.
Function a(b) {
const c = b();
return c;
}
test.js
require("sinon")
describe("a", () => {
const stub = sinon.stub();
stub.returns("DummyValue");
expect(a(stub)).to.eql.("DummyValue");
})
Note that we can use const for these variable declarations as they are never being reassigned.

Categories