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.
Related
I have the following scenario:
const doSomething = () => {
const test = 1;
doAnotherThing();
return test + 1;
};
const doAnotherThing = () => {};
module.exports = {
doSomething,
doAnotherThing
};
And here is my test:
const MyModule = require('./myModule');
describe('MyModule', () => {
it('Should be able to spy another call', () => {
const spy = jest.spyOn(MyModule, 'doAnotherThing');
MyModule.doSomething();
expect(spy).toHaveBeenCalledTimes(1);
});
});
Question, is there a way for the call doAnotherThing() inside the doSomething() be spied by jest in some way, without using solutions like rewire?
Found the solution here: https://medium.com/welldone-software/jest-how-to-mock-a-function-call-inside-a-module-21c05c57a39f
Redefined my module to this and now it's working
const doSomething = () => {
const test = 1;
MyModule.doAnotherThing();
return test + 1;
};
const doAnotherThing = () => {};
const MyModule = {
doSomething: doSomething,
doAnotherThing: doAnotherThing
};
export default MyModule;
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
});
});
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;
})
zoomOut(callback) {
// Zooms out the current screen
this.view.current.zoomOut(300).done(() => {
(hasCallback(callback)) && callback();
});
}
I'm trying to test the function above but I keep getting the following error:
TypeError: this.view.current.zoomOut(...).done is not a function
How can I mock this method chain in Jest?
Thanks to BudgieInWA, I was able to solve this problem by returning done.
For those who are testing a React component with Enzyme, here's how you can do it:
it('should call callback', () => {
const wrapper = shallow(<Zoom {...minProps}/>);
const instance = wrapper.instance();
const callback = jest.fn();
instance.view = {
current: {
zoomOut: jest.fn(() => {
return {
done: jest.fn((callback) => {
callback();
})
};
})
}
};
expect(callback).toHaveBeenCalledTimes(0);
instance.zoomOut(callback);
expect(callback).toHaveBeenCalledTimes(1);
});
You could try this:
const mockZoomOut = jest.fn(() => ({ done(cb) { cb(); } }));
const mockThis = {
view: {
current: {
zoomOut: mockZoomOut,
},
},
};
test('it does', () => {
const cb = jest.fn();
zoomOut.apply(mockThis, [cb]);
expect(mockZoomOut).toHaveBeenCalledTimes(1);
expect(cb).toHaveBeenCalledTimes(1);
});
See Jest Mock Functions and fn.apply.
If you are testing the behaviour of the class as a whole, then you could set up the instance that you are testing to have this.view.current.zoomOut be mockZoomOut somehow.
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)