How do I mock this method chain in Jest? - javascript

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.

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 check function is called or not in react js?

I am trying to test my service which have one function saveWithoutSubmit
export const saveWithoutSubmit = async (
values,
orderId,
taskId,
fseMsisdn,
updateTaskListAfterFilter
) => {
var obj = {
remarks: values.remarks,
requestedBy: localStorage.getItem("msisdn")
};
try {
const response = await sendPostRequest(`${API_TASK_URL}closeSr`, {
...obj,
saveWithoutSubmit: true
});
if (response && response.data && response.data.status.code !== "200") {
error(response.data.result.message);
} else {
console.log(response);
success(response.data.status.message);
updateTaskListAfterFilter();
}
} catch (e) {
if (e.response && e.response.data) {
console.log(e.response.data.message);
error(e.response.data.status.message);
}
}
};
I want to check success or error method is called or not ? or updateTaskListAfterFilter is called or not?
I tried like this
https://codesandbox.io/s/ecstatic-currying-5q1b8
describe("remark service test", () => {
const fakeAxios = {
get: jest.fn(() => Promise.resolve({ data: { greeting: "hello there" } }))
};
it("save without sumit", () => {
const updateTaskListAfterFilter = () => {};
saveWithoutSubmit({}, updateTaskListAfterFilter);
expect(updateTaskListAfterFilter).toBeCalled();
});
});
can you please suggest how i will test async methods or post request (using mook data)??
so that my test cases will be passed.
I want to check if I got success from promise my success method will be called else error
any update ?..!!
update
https://codesandbox.io/s/ecstatic-currying-5q1b8
it("save without sumit", async () => {
const sendPostRequest = jest.fn(() =>
Promise.resolve({ data: { greeting: "hello there" } })
);
const updateTaskListAfterFilter = () => {};
saveWithoutSubmit({}, updateTaskListAfterFilter);
expect(updateTaskListAfterFilter).toBeCalled();
});
it("save without sumit", async () => {
const sendPostRequest = jest.fn(() =>
Promise.resolve({ data: { greeting: "hello there" } })
);
const mockUpdateTaskListAfterFilter = jest.fn();
const updateTaskListAfterFilter = () => {};
saveWithoutSubmit({}, updateTaskListAfterFilter);
expect(updateTaskListAfterFilter).toBeCalled();
await wait(() => {
expect(mockUpdateTaskListAfterFilter).toBeCalled();
});
});
You should change it("save without sumit", () => { to it("save without sumit", async () => {.
Then you usually use jest.fn() to create a mock function that you will give to another function or component.
Finally, await the mock function to be called:
await wait(() => {
expect(mockUpdateTaskListAfterFilter).toBeCalled();
});
Alternatively, you can await some other event that you know will occur before your mock is called, like some other mock getting called or something appearing on the page, and then check that your mock was called.

Mock node external module default with chained method using jest

In our node CLI we have a simple method:
'use strict';
const ora = require('ora');
module.exports = function startSpinner({ textOnStart, color, spinnerType }) {
const spinner = ora({
text: textOnStart,
color: color || 'cyan',
spinner: spinnerType || ''
}).start();
};
We try to use jest to test this method. We have two tests to achieve:
Testing that ora has been called with proper object argument
Testing that the method start() was called afterward
That being said we cannot achieve to mock ora module properly.
ora is a third party that is basically constructed as follow:
class Ora {
constructor(options){}
start(){ }
}
const oraFactory = function (opts) {
return new Ora(opts);
};
module.exports = oraFactory;
module.exports.default = oraFactory;
We are looking for a way to mock ora.
We tried to use auto mock:
const ora = require('ora');
jest.mock('ora');
const startSpinner = require('./startSpinner');
describe('startSpinner', () => {
beforeEach(() => {
startSpinner({});
});
describe('ora', () => {
it('should call ora', () => {
expect(ora).toHaveBeenCalled();
});
it('should call ora start', () => {
expect(ora.start).toHaveBeenCalled();
});
});
});
But both tests fail with respectively:
Matcher error: received value must be a mock or spy function
Received has type: function
Received has value: [Function oraFactory]
and
Matcher error: received value must be a mock or spy function
Received has value: undefined
We tried to use a custom mock:
const ora = require('ora');
jest.mock('ora', () => {
return jest.fn().mockImplementation(() => {
return { start: jest.fn() };
});
});
and it ends up with the same exact result.
We even tried to convert our test to typescript and then use:
import * as ora from 'ora';
const startMock = jest.fn();
jest.mock('ora', () => {
return jest.fn().mockImplementation(() => {
return { start: startMock };
});
});
Then we were able to test successfuly that ora was called. But we ended up with an error for expect(ora.start).toHaveBeenCalled(); or even expect((ora as any).start).toHaveBeenCalled();:
error TS2339: Property 'start' does not exist on type 'typeof
import("/Users/Dev/cli/node_modules/ora/index")'.
Surely caused by the fact the type definition of imported ora is export default function ora(options?: Options | string): Ora;
How to then mock a third party like ora in jest's node test environnement?
You've got a couple of options:
You can mock ora like this:
jest.mock('ora', () => {
const start = jest.fn();
const result = { start };
return jest.fn(() => result);
});
...and then call ora to get the object it returns (since it always returns the same object) and use that object to access start:
it('should call ora start', () => {
const result = ora();
expect(result.start).toHaveBeenCalled(); // Success!
});
Or if you want you can attach the start mock as a property to the ora mock as an easy way to access it during your tests like this:
const ora = require('ora');
jest.mock('ora', () => {
const start = jest.fn();
const result = { start };
const ora = jest.fn(() => result);
ora.start = start; // attach the start mock to ora
return ora;
});
const startSpinner = require('./startSpinner');
describe('startSpinner', () => {
beforeEach(() => {
startSpinner({});
});
describe('ora', () => {
it('should call ora', () => {
expect(ora).toHaveBeenCalled(); // Success!
});
it('should call ora start', () => {
expect(ora.start).toHaveBeenCalled(); // Success!
});
});
});

spying a function called in a rejected promise

I would like to spy a function to test if this function is called in a catch block when a promise is rejected. My code is a react component like this
export class ResetPassword extends Component {
handleSubmit = e => {
e.preventDefault();
this.props
.resetPassword()
.then(() => {
this.props.history.push(LOGIN_PATH);
})
.catch(({ error }) => {
this.props.displayErrorAlert('impossible to change password. You should ask for a new reset password link',6000);
});
};
}
Here I want to test if the function displayErrorAlert has been called. I made this test
it('validate form', () => {
const resetPassword = () => {
return Promise.reject({
error: {
response: {
data: {
errors: [
{
title: 'foo',
},
],
},
},
},
});
};
const displaySpy = sinon.spy();
const wrapper = mount(
<ResetPassword
history={{}}
resetPassword={resetPassword}
displayErrorAlert={displaySpy}
/>
);
wrapper.instance().handleSubmit({
preventDefault: () => {},
});
expect(displaySpy.calledOnce).toEqual(true);
});
The spy is called but asynchronously of course so my test always fails. I would like to find a way to test if the function has been called only once the catch block has been called and I have no idea how to do that.
Sinon provides you everything you need when handling promises, you can resolve and reject a stubbed promise using sinon.stub().
const resetPassword = sinon.stub();
const displayErrorAlert = sinon.spy();
const preventDefault = sinon.spy();
const props = {
resetPassword,
displayErrorAlert,
history: []
};
describe('Given a component', () => {
let component;
describe('when rendered', () => {
beforeAll(() => {
component = shallow(<ResetPassword {...props} />);
});
describe('and the form is submitted and there is an error reseting the password', () => {
beforeAll(() => {
resetPassword.rejects(new Error('Oops!'));
component.find('button').simulate('click', { preventDefault });
});
it('should invoke the displayErrorAlert function', () => {
expect(displayErrorAlert.calledOnce).toBeTruthy();
});
});
});
});
I found an other solution, I return the promise in the handleSubmit function and use it in my tests.
export class ResetPassword extends Component {
handleSubmit = e => {
e.preventDefault();
return this.props
.resetPassword()
.then(() => {
this.props.history.push(LOGIN_PATH);
})
.catch(({ error }) => {
this.props.displayErrorAlert('impossible to change password. You should ask for a new reset password link',6000);
});
};
}
and my test
it('validate form', () => {
const resetPassword = () => {
return Promise.reject({
error: {
response: {
data: {
errors: [
{
title: 'foo',
},
],
},
},
},
});
};
const displaySpy = sinon.spy();
const wrapper = mount(
<ResetPassword
history={{}}
resetPassword={resetPassword}
displayErrorAlert={displaySpy}
/>
);
expect.assertions(1);
const promise = wrapper.instance().handleSubmit({
preventDefault: () => {},
});
return promise.then(() => {
expect(displaySpy.calledOnce).toEqual(true);
});
});

fetch-mock with jasmine not triggering then

I have this constant:
export const clientData = fetch(`${process.env.SERVER_HOST}clientData.json`)
.then(response => response.json());
Which works properly, and Now I'm working on the test of this, with Jasmine and fetch-mock
This is my test:
import { clientData } from '../../../src/js/services/client-data.fetch';
import fetchMock from 'fetch-mock';
describe('test', () => {
const exampleResponse = {
clientData: 'test'
};
beforeAll(() => {
fetchMock.mock('*', exampleResponse);
});
it('ooo', () => {
console.log('here', clientData);
var a = clientData;
a.then(b=> console.log(b))
});
});
The console.log of clientData returns a Promise (Which is fine), but the then is never triggered.
Not seeing why, what is wrong with my code?
This happens because the test execution is synchronous in nature and it doesn't wait for the assertion to happen, so you have to pass a done callback and call it from your test inside the then callback
Like this:
import { clientData } from '../../../src/js/services/client-data.fetch';
import fetchMock from 'fetch-mock';
describe('test', () => {
const exampleResponse = {
clientData: 'test'
};
beforeAll(() => {
fetchMock.mock('*', exampleResponse);
});
it('ooo', (done) => {
console.log('here', clientData);
var a = clientData;
a.then(b=> {
console.log(b);
done();
})
});
});

Categories

Resources