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.
Related
useEffect(() => {
const getCategory = () => {
allcategory().then((res) => {
if (res.data.error) {
console.log("error");
setValues({ ...values,
message: res.data.error
});
} else {
setValues({ ...values,
categories: res.data.category,
formData: new FormData(),
})
}
})
}
const getPublisher = () => {
allpublisher().then((res) => {
if (res.data.error) {
console.log("error");
setValues({ ...values,
message: res.data.error
});
} else {
setValues({ ...values,
publishers: res.data.publisher,
formData: new FormData(),
});
}
})
}
getCategory()
getPublisher()
}, [])
When I try to add two function on useEffect it doesn't render any data but show data on first render if only one function is present on useEffect
Since you are using promises, you can implement promise chaining here like
getCategory().then(()=>getPublisher())
But instead of having multiple promise chains, you can implement async await functions
const getData = async() => {
await getCategory()
await getPublisher()
}
useEffect(() => {
getData()
},[])
I think you miss async function.
I think this should like this
const getCategory = async () =>
const getPublisher = async () =>
And with my opinion, you should refractor like this:
const getCategory = async () => {
// ...do sth
}
const getPublisher = async () => {
// ...do sth
}
useEffect(() => {
getCategory();
getPublisher();
},[])
I have the following cusotm React hook:
...
useEffect(() => {
const handleMonitoringData = async (isDefaultProduct?: boolean) => {
const result = await getMonitoringData(intermediaryId);
if (result) {
const sortedResult = result.sort((a, b) =>
a.product?.name > b.product?.name ? 0 : -1
);
setMonitoringData(sortedResult);
if (isDefaultProduct) selectProduct(sortedResult[0]);
}
};
if (isSuperUser) {
setMonitoringData([]);
selectProduct(null);
if (hasRendered) {
handleMonitoringData();
} else {
toggleHasRendered(true);
}
} else {
handleMonitoringData(true);
}
}, [intermediaryId]);
...
and my attempt at testing the initial monitoring data load (precisely the else statement => handleMonitoringData(true)) like so:
jest.mock('#api/Monitoring', () => ({
getMonitoringData: () => [mockedData],
}));
describe('useFundRaising custom hook', () => {
it('should work', async () => {
function TestComponent() {
const { monitoringData } = useFundRaising();
return <div>{console.log('data: ', monitoringData)}</div>;
}
const res = await render(<TestComponent />);
});
});
getMonitoringData:
export const getMonitoringData = async (
intermediaryId?: string
): Promise<MonitoringData[]> => {
const URL = intermediaryId
? `${MONITORING_DATA_URL}/${intermediaryId}`
: MONITORING_DATA_URL;
const result = await Http.get<MonitoringData[]>(URL);
return result;
};
the test is currently failing:
[![enter image description here][2]][2]
Have you tried to mock like this:
const mockGetMonitoringData = jest.fn().mockResolvedValue(mockedData);
jest.mock('#api/Monitoring', () => ({
getMonitoringData: () => mockGetMonitoringData(),
}));
as the getMonitoringData is an async method.
im using an http request function as the handler function in middy and then use the ssm middleware to fetch some ssm parameters before initiating the http request.
like this:
const makeThirdPartyServiceRequest = middy(async ({ params }) => {
logger.info(`SENDING Request to ${endpoint} API`)
const url = `https://someurltoathirdpartyservice`
const options = {
method: 'POST',
body: params
}
return helpers.makeRequest(url, options)
})
makeThirdPartyServiceRequest.use(ssm(......))
However in my jest unit test Im trying to mock makeThirdPartyServiceRequest and explicitly say it should resolve to a value:
jest.mock('../src/thirdPartyService', () => ({
__esModule: true,
default: {
...(jest.requireActual('../src/thirdPartyService') as { default: {} }).default,
makeThirdPartyServiceRequest: jest.fn()
}
}))
export {}
import thirdPartyService from '../src/thirdPartyService'
And then in the test i say:
describe('makeThirdPartyServiceRequest()', () => {
it('should makeThirdPartyServiceRequest', async () => {
// Given
// })
const mockedThirdPartyServiceRequest = mocked(thirdPartyService.makeThirdPartyServiceRequest).mockResolvedValue({})
// When
const result = await thirdPartyService.makeThirdPartyServiceRequest(something)
// Then
expect(mockedThirdPartyServiceRequest).toHaveBeenCalledTimes(1)
expect(mockedThirdPartyServiceRequest.mock.calls[0][0].params.toString()).toBe(expectedParams)
})
})
However for some reason the middy middleware is still being invoked, which i clearly dont want and i have tried to mock away... what am i doing wrong?
You need to mock middy instead, to make it becomes a useless function. That function recipe a function as a parameter and return that parameter.
import thirdPartyService from '../src/thirdPartyService'
jest.mock('#middy/core', () => {
return (handler) => {
return {
use: jest.fn().mockReturnValue(handler), // ...use(ssm()) will return handler function
}
}
})
describe('thirdPartyService()', () => {
beforeEach(() => {
jest.spyOn(helpers, 'makeRequest') // spy on helpers unit
})
describe('makeThirdPartyServiceRequest', () => {
it('should make a request with correct parameters', async () => {
// Given
const url = `https://someurltoathirdpartyservice`
const params = 'any params'
const apiResponse = 'any response'
mocked(helpers.makeRequest).mockResolvedValue(apiResponse)
// When
const actual = await thirdPartyService.makeThirdPartyServiceRequest(params)
// Then
expect(actual).toBe(apiResponse)
expect(helpers.makeRequest).toHaveBeenCalledWith(
url,
{
method: 'POST',
body: params
}
)
})
})
})
hoangdv answer is also valid, but i will answer as well how i continued.
if you completely want to mock middy you mock like following:
jest.mock('#middy/core', () => {
return (handler) => {
return {
use: jest.fn().mockImplementation(() => {
// ...use(ssm()) will return handler function
return {
before: jest.fn().mockReturnValue(handler)
}
})
}
}
})
However if you dont want to completely mock middy, you can instead mock the async getInternal function from middy/util called in before like this:
jest.doMock('#middy/util', () => ({
...(jest.requireActual('#middy/util') as {}),
getInternal: jest.fn()
}))
import { getInternal } from '#middy/util'
and then in the test
describe('thirdPartyService()', () => {
beforeEach(() => {
jest.spyOn(helpers, 'makeRequest') // spy on helpers unit
})
describe('makeThirdPartyServiceRequest', () => {
it('should make a request with correct parameters', async () => {
// Given
const url = `https://someurltoathirdpartyservice`
const params = 'any params'
const apiResponse = 'any response'
mocked(getInternal).mockResolvedValue({
twilioSecrets: { accountSid: 'someSID', serviceId:
'someServiceID', token: 'someToken' }
})
mocked(helpers.makeRequest).mockResolvedValue(apiResponse)
// When
const actual = await thirdPartyService.makeThirdPartyServiceRequest(params)
// Then
expect(actual).toBe(apiResponse)
expect(helpers.makeRequest).toHaveBeenCalledWith(
url,
{
method: 'POST',
body: params
}
)
})
})
})
this will mock the async part of middy.
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
});
});
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.