Testing nested function in Jest - javascript

I have in file.js code which I simplify this way:
function Apple(,data) {
this.attr1 = data.attr1,
this.attr2 = data.attr2,
this.doSomething(data.flag);
}
Apple.prototype = new function() {
this.function1 = function() {
// do something
}
this.doSomething = function(flag) {
// return value
}
}
I want to test function1(), for that, I want to mock first doSomething() to return a specific value, but I am failing because any call to Apple() function executes immediately doSomething():
describe('Apple', () => {
test('test function1()', () => {
Apple.doSomething = jest.fn().mockImplementation((2) => {
// do something;
});
let apple = new Apple(data); // the original doSomething() executes here instead of the mocked version
});
});
How can I achieve my goal ?

Try using spyOn:
const mockDoSomething = jest.spyOn(Apple.prototype, 'doSomething').mockReturnValueOnce((flag) => {
// mock do something;
})

Related

expect(jest.fn()).toHaveBeenCalled() fails even though the function has been called

I'm trying to unit test a function that returns a promise. I'm having challenges verifying if
the mocked functions are called. Here's what I've done.,
// codetotest.js
const { SomeModule, isSomething, isSomethingElse } = require("some-module");
exports.somefunction = (param1, param2)=> {
const someModule = new SomeModule();
someModule.someMethod("aaa", isSomething);
someModule.someMethod("bbb", isSomethingElse);
return (someModule.someOtherMethod(param1)
.then(()=>{someModule.run(param2)}));
}
And this is the test file, the test says the mocked functions are not called, but I do see the console statement in the mock function is being displayed.
// codetotest.test.js
const { somefunction} = require("./codetotest.js");
const { SomeModule } = require("some-module");
jest.mock("some-module", () => {
return {
SomeModule: jest.fn().mockImplementation(() => {
return {
someMethod: jest.fn((param, fn) => { console.log("This prints!"); }),
someOtherMethod: jest.fn((param) => { return Promise.resolve(() => { }) }),
run: jest.fn((param) => { return Promise.resolve(() => { return []; }) })
}
})
};
});
afterEach(() => {
jest.resetAllMocks();
jest.restoreAllMocks();
});
describe("Test codetotest.js", () => {
it("somefunction() - success", async () => {
const someModule = new SomeModule();
let output = await somefunction("param1", "param2");
expect(SomeModule).toHaveBeenCalled();
expect(someModule.someMethod).toHaveBeenCalled(); // This fails
await expect(someModule.someOtherMethod.mock.results[0]).resolves;
expect(someModule.someOtherMethod).toHaveBeenCalled(); // This fails
await expect(someModule.run.mocks.results[0]).resolves;
expect(someModule.run).toHaveBeenCalled(); // This fails
});
});
Appreciate any help/pointers.
Thank you.
P.S: I'm still a beginner when it comes to nodeJs development and unit testing.
I spent quite some time on this and finally figured the instantiated mock class didn't return the mocked methods properly. This answer gave me a hint on where I was going wrong.
So accordingly, I had to change my test file as follows.,
// codetotest.test.js
const { somefunction} = require("./codetotest.js");
const { SomeModule } = require("some-module");
jest.mock("some-module", function() {
return {
SomeModule: jest.fn().mockImplementation(function() { // Arrow function cannot be used as constructor
// Because I was not using the 'this' operator, my constructor always returned empty
this.someMethod = jest.fn((param, fn) => { console.log("This prints!"); });
this.someOtherMethod = jest.fn((param) => { return Promise.resolve(() => { }) });
this.run = jest.fn((param) => { return Promise.resolve(() => { return []; }) });
return {
someMethod: this,someMethod,
someOtherMethod: this.someOtherMethod,
run: this.run
}
})
};
});
afterEach(() => {
jest.restoreAllMocks();
});
describe("Test codetotest.js", () => {
it("somefunction() - success", async () => {
await somefunction("param1", "param2");
expect(SomeModule).toHaveBeenCalled();
expect(SomeModule.mock.instances[0].someMethod).toHaveBeenCalled(); // This works
expect(SomeModule.mock.instances[0].someOtherMethod).toHaveBeenCalled(); // This works
expect(someModule.mock.instances[0].run).toHaveBeenCalled(); // This works
});
});

How to make a nested function call within an IIFE?

const parkReport = () => {
return {
averageAge: () => {
return console.log('Something');
}
}
};
const initialize = ( parkReport => {
return parkReport.averageAge();
})(parkReport);
In the IIFE initialize parkReport.averageAge() is showing an error of not a function. How do you call the nested AverageAge() from initialize?
You need to call the parkReport function. Here you are passing parkReport as a callback to the IIFE. so you need to call it to expect something returned from it.
const parkReport = () => {
return {
averageAge: () => {
return console.log('Something');
}
}
};
const initialize = (parkReport => {
return parkReport() // call it
.averageAge(); // and get the age
})(parkReport);

Sinon spy does not get called

The test is failing on the first expectation. Is there a way to inject a spy to a function so that I can check if the function was called with correct arguments?
var myObj = {}
myObj.prop = function propFn () {
return 'foo'
}
myObj.func = function (disp) {
return disp(this.prop())
}
let disp = sinon.spy()
sinon.stub(myObj, 'prop').callsFake(function fakeFn () {
return 'bar'
})
expect(disp.called).to.be.true
disp.should.have.been.calledWith('bar')
Thanks!
Please try as below,
describe('prop', () => {
const myObj = {};
myObj.prop = function propFn() {
return 'foo';
};
myObj.func = function (disp) {
return disp(this.prop());
};
it('should be called', () => {
sinon.stub(myObj, 'prop').returns('bar');
const disp = sinon.spy();
myObj.func(disp);
expect(disp.callCount).to.equal(1);
expect(myObj.prop.callCount).to.equal(1);
expect(disp.callCount).to.equal(1);
expect(disp.calledWith('bar')).to.equal(true);
});
});
result
prop
✓ should be called
1 passing (975ms)

Stubs and proxquire

I'm having an issue with stubbing a particular function with sinon after having used proxyquire.
Example:
// a.js
const api = require('api');
module.exports = (function () {
return {
run,
doStuff
};
function run() {
return api()
.then((data) => {
return doStuff(data);
})
}
function doStuff(data) {
return `Got data: ${data}`;
}
})()
// a.spec.js - in the test
a = proxyquire('./a', {
'api': () => Promise.resolve('data')
})
sinon.stub(a, 'doStuff');
// RUN TEST - call a.run()
I know it isn't working because it calls the original doStuff instead of a mocked/stubbed doStuff.
I know it isn't working because it calls the original doStuff instead
of a mocked/stubbed doStuff.
That is because function run() in a.js invokes function doStuff(data) inside the closure function run() is holding over the content of a.js and not function doStuff(data) in a_spec.js.
Illustration by example
Lets re-write a.js to:
var a = 'I am exported';
var b = 'I am not exported';
function foo () {
console.log(a);
console.log(this.b)
}
module.exports.a=a;
module.exports.foo=foo;
and a.spec.js to:
var one = require('./one');
console.log(one); // { a: 'I am exported', foo: [Function: foo] }
one.a = 'Charles';
one.b = 'Diana';
console.log(one); // { a: 'Charles', foo: [Function: foo], b: 'Diana' }
Now if we invoke one.foo() it will result in:
I am exported
Diana
The I am exported is logged to the console because console.log(a) inside foo points to var a inside the closure foo is holding over the contents of a.js.
The Diana is logged to the console because console.log(this.b) inside foo points to one.b in a.spec.js.
So what do you need to do to make it work?
You need to change:
module.exports = (function () {
return {
run,
doStuff
};
function run() {
return api()
.then((data) => {
return doStuff(data);
})
}
function doStuff(data) {
return `Got data: ${data}`;
}
})()
to:
module.exports = (function () {
return {
run,
doStuff
};
function run() {
return api()
.then((data) => {
return this.doStuff(data); // ´this.doStuff´ points to ´doStuff´ in the exported object
}.bind(this)) // to ensure that ´this´ does not point to the global object
}
function doStuff(data) {
return `Got data: ${data}`;
}
})()

Sinon.spy on a method is not invoked

The code I'm testing is fairly simple: it invokes a method in case a condition is verified. If not, it invokes another method contained within the first one as an attribute.
app.js:
function test (fn, isActivated) {
if (isActivated) {
return fn('foo')
}
return fn.subFn('bar')
}
var fn = function (p) { return p }
fn.subFn = function (p) { return 'sub-' + p }
var resFn = test(fn, true)
var resSubFn = test(fn, false)
document.write(resFn) // shows 'foo' as expected
document.write(resSubFn) // shows 'bar' as expected
I've set a spy on each method but the spy on the fn method does not seem to work while the spy on the contained method subFn works. See below:
app.test.js:
'use strict'
const chai = require('chai')
const sinon = require('sinon')
const trigger = require('../app').trigger
chai.should()
describe('test app', function () {
before(function () {
this.fn = function () {}
this.fn.subFn = function () {}
this.subFnSpy = sinon.spy(this.fn, 'subFn')
this.fnSpy = sinon.spy(this.fn)
})
describe('isActivated is true', function () {
before(function () {
trigger(this.fn, true)
})
it('should invoke fn', function () {
this.fnSpy.callCount.should.equal(1) // return false because callCount = 0
})
})
describe('isActivated is false', function () {
before(function () {
trigger(this.fn, false)
})
it('should invoke subFn', function () {
this.subFnSpy.callCount.should.equal(1) // return false because callCount = 0
})
})
})
Smelling something wrong with the spy on the fn function, I've tried with two separate methods. Both spies fail in this case:
app.js:
exports.trigger = function (fn, subFn, isActivated) {
if (isActivated) {
return fn('fn')
}
return subFn('bar')
}
app.test.js
'use strict'
const chai = require('chai')
const sinon = require('sinon')
const trigger = require('../app').trigger
chai.should()
describe('test app', function () {
before(function () {
this.fn = function () {}
this.subFn = function () {}
this.fnSpy = sinon.spy(this.fn)
this.subFnSpy = sinon.spy(this.subFn)
})
beforeEach(function () {
this.fnSpy.reset()
this.subFnSpy.reset()
})
describe('isActivated is true', function () {
before(function () {
trigger(this.fn, this.subFn, true)
})
it('should invoke fn if isActivated is true', function () {
this.fnSpy.callCount.should.equal(1) // return false
})
})
describe('isActivated is false', function () {
before(function () {
trigger(this.fn, this.subFn, false)
})
it('should invoke subFn if isActivated is true', function () {
this.subFnSpy.callCount.should.equal(1) // return false
})
})
})
Any suggestion of what I'm doing wrong?
I did not find the exact solution but a workaround quite close from one. So the problem seems to lie with the way this.fn is handled withtin sinon.spy, thus instead of doing:
this.fnSpy = sinon.spy(this.fn)
this.subFnSpy = sinon.spy(this.subFn)
We do the following:
this.fnSpy = sinon.spy(this, 'fn')
this.subFnSpy = sinon.spy(this.fn, 'subFn')
The is easd by the fact that I use this to store fn and subFn.

Categories

Resources