I'm trying to use node-geocoder npm package to get latitude and longitude of position some objects.
Could you help me please to understand JavaScript promises.
Here is an example code:
import nodeGeocoder from 'node-geocoder'
function getLocation (str) {
// Selecting Google as provider
const geocoder = nodeGeocoder({
provider: 'google',
})
return geocoder.geocode(str)
.then(response => {
// I want to return latitude an longitude
return [response[0].latitude, response[0].longitude]
})
.catch(error => {
console.log(`Geocoder Error: ${ error }`)
})
}
export default getLocation
So. This is a test (Jest framework):
import getLocation from './index'
test('Checking', () => {
expect(getLocation('29 champs elysée paris')).toEqual([48.8698679, 2.3072976])
})
When I'm trying to use this test I just get promise status {"fulfillmentValue": undefined, "isFulfilled": false, "isRejected": false, "rejectionReason": undefined}.
But I need to get just promise resolved result. How can I do it?
I don't want to edit test
For testing, most test suites offer an async callback for all your promise based tests. I would expect it (pun intended) to work like this:
import getLocation from './index'
test('Checking', (done) => {
getLocation('29 champs elysée paris').then(geoData => {
expect(geoData).toEqual([48.8698679, 2.3072976]);
done();
}).catch(error => {
done(error)
});
});
Depending on the testing framework you might be using, the way you call the resolver callback (i.e: done()) can change. However, the pattern should more or less be the same.
Related
Trying to write a test to provide code coverage for the following code :
note : there are other functions in the service but just listing one for brevity.
export const service = {
getById: async (id) => {
const url = `/api/customers/${id}/names`
const {data} = await axios.get(url, axiosOptions);
return data;
}
I'm attempting to simply provide code coverage with this test:
note : I have attempted to use require instead of import but that does not seem to work.
import {service} from './requests';
it("mocks the getById function", () => {
service.getById = jest.fn();
expect(service.getById.mock).toBeTruthy();
}
This test passes however seems to provide no code coverage.
I've attempted to mock out the axios call but I seem to get nowhere as examples I've found of implementations are not working for me currently.
Does anyone have ideas and an example how I could provide code coverage for the service please?
Update : to sonEtLumiere's answer
jest.mock('./service', () => ({
getById: jest.fn().mockResolvedValue({ data : "hello"}),
}));
describe('test', () => {
it('mocks the service", async () => {
service.getById.mockResolvedValue({data: "hello});
const data = await service.getById(1);
expect(data).toEqual({data:"hello"});
})
})
Currently getting back error :
Cannot read properties of undefined (reading 'getById')
Any thoughts on why I'm getting this error?
To mock a service using Jest, you can use the jest.mock() function to create a mocked version of the service. For example:
jest.mock('path/to/service', () => ({
getById: jest.fn().mockResolvedValue({ /* mocked data */ }),
}));
Then, in your test file, you can import the mocked version of the service and use the mock property on the function to control its behavior. For example, you can use .mockResolvedValue to set the resolved value of the function, or use .mockRejectedValue to make the function throw an error.
import { service } from 'path/to/service';
describe('test', () => {
it('mocks the service', async () => {
service.getById.mockResolvedValue({ /* mocked data */ });
const data = await service.getById(1);
expect(data).toEqual({ /* mocked data */ });
});
});
I do agree with #Lin Du's comment, if you want to test service.getById, you should be mocking what the method depends on, in this case axios.get.
But following along with your question, the issue is that the named export in ./requests is an object containing the getById property which is the method you want to test. So jest.mock should look like:
jest.mock("./requests.js", () => ({
service: {
getById: jest.fn(),
},
}))
Then your test will pass as you expected:
it("mocks the getById function", async () => {
service.getById.mockResolvedValueOnce({ data: "hello" })
const data = await service.getById(1)
expect(data).toEqual({ data: "hello" })
})
But again, if you want to test a method and have proper coverage, what you need to mock is the method's dependency, not the method itself, e.g:
import { service } from "./requests"
import axios from "axios"
jest.mock("axios")
test("service.getById", async () => {
axios.get.mockResolvedValueOnce({ data: "hello" })
const result = await service.getById(1)
expect(result).toBe("hello")
})
I have the following function that uses fetch() to make an API call:
export async function fetchCars(dealershipId) {
return request('path/to/endpoint/' + dealershipId)
.then((response) => {
if (response.ok === false) {
return Promise.reject();
}
return response.json();
})
.then((cars) => {
return parseMyCars(cars);
});
}
I want to test when the call fails (specifically when return Promise.reject() is returned). I have the following Jest test right now:
(fetch as jest.Mock).mockImplementation(() =>
Promise.resolve({ ok: false })
);
const result = await fetchCars(1);
expect(request).toHaveBeenCalledWith('/path/to/endpoint/1');
expect(result).toEqual(Promise.reject());
but I get a Failed: undefined message when running the test. I've tried using:
(fetch as jest.Mock).mockRejectedValue(() =>
Promise.resolve({ ok: false })
);
but get a similar Failed: [Function anonymous] message.
What's the proper way to test for the rejected promise here?
Your test is failing because it's throwing an exception. You need to catch that exception, there are many ways to do it, and using rejects helper is one of them:
it("should fail", async () => {
(window.fetch as jest.Mock).mockResolvedValueOnce({
ok: false,
});
await expect(fetchCars(1)).rejects.toEqual(/* error you are expecting*/);
});
There is a couple of ways to deal with this correctly. I think the issue is that you are awaiting your promise.
If you plan to use await then you must await the expect:
const result = fetchCars(1);
await expect(result).toEqual(Promise.reject());
// ✅ Correct setup
or you can just return the un-awaited promise:
const result = fetchCars(1);
return expect(result).toEqual(Promise.reject());
// ✅ Correct setup
If you don't return or await the expect, then you might notice you get a false positive:
const result = fetchCars(1);
expect(result).toEqual(Promise.reject());
// ❌ False positive!
I've created a codesandbox example showing you how to mock the fetch function and how to correctly set up the test including the incorrect ways. Have a play around with the Tests tab at the top right.
I have a function like this:
join(): void {
this.working.value = true;
if (this.info.value) {
axios.get('/url')
.then((result: ResultStatus) => {
this.result = result;
})
.catch((reason: AxiosError) => {
this.showError(AjaxParseError(reason));
})
.finally(() => {
this.working.value = false;
});
}
}
and I want to write some unit tests for this. The first unit test I want to write is to test that 'this.saving' is set to true so that I ensure my UI has a value it can use to show a loading indicator.
However, when I use jest to mock axios, jest resolves the axios promise immediately and I don't have a chance to test what happens before the then/finally block is called. Here is what my unit test code looks like:
import axios from 'axios';
jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;
import successResponse from './__json__/LeagueJoinInfoSuccess.json';
describe('constructor:', () => {
let vm: classUnderTest;
beforeEach(() => {
vm = new classUnderTest();
mockedAxios.get.mockResolvedValue({ data: successResponse }); // set up the response
vm.join(); // the function under test
});
it('should set working true before calling the server to join', () => {
expect(vm.working.value).toBeTruthy();
});
it('should set working false after calling the server responds', async () => {
await flushPromises();
expect(vm.working.value).toBeFalsy();
});
});
The first expect statement is always false because the finally block is run before I have a chance to do an await flushPromises(); so the working value is always false.
Is there a convenient way to get jest's mock of axios to wait before resolving its promise?
UPDATE: Now here is a really strange thing: If I move the contents of BeforeEach into each of the tests, then it behaves the way that I am hoping it would behave. I guess I will open an issue over at jest and ask them what's going on.
I have a solution for you to create a promise as response, however, we're not gonna resolve it in the 1st test case to keep you test loading state then resolve it in the 2nd test as following:
describe('constructor:', () => {
let vm: classUnderTest;
// Resolve function
let resolveFn: (value?: unknown) => void
beforeEach(() => {
vm = new classUnderTest();
const promise = new Promise(resolve => {
// hold the resolve function to call in 2nd test
resolveFn = resolve;
});
mockedAxios.get.mockImplementation(() => promise);
vm.join(); // the function under test
});
it('should set working true before calling the server to join', () => {
expect(vm.working.value).toBeTruthy();
});
it('should set working false after calling the server responds', async () => {
// resolve the pending promise
resolve({
data: successResponse,
});
// I think you would wait the promise resolved
await new Promise(resolve => setTimeout(resolve));
expect(vm.working.value).toBeFalsy();
});
});
I am new to jest/enzyme and I try to test a function. The test is attempting to test that getData() sets the array returned in the state.
This is how it looks :
getData () {
axios.get('http://localhost:8080/targeting-data').then(response => {
this.setState({
targetingData: response.data,
load: true
})
})
}
componentDidMount () {
this.interval = setTimeout(() => {
this.getData()
}, 3000)
}
My first test :
import FormWrapper from './../containers/FormWrapper/FormWrapper'
const mockGetData = jest.spyOn(FormWrapper.prototype, 'getData')
it('testing', () => {
mockGetData()
expect(formWrapper.state().targetingData).toHaveLength(8)
})
And I got fails :
expect(received).toHaveLength(length)
Expected value to have length:
8
Received:
[]
received.length:
0
My second test :
it('getDataFunc', (done) => {
mockGetData().then(res => {
expect(res).toHaveLength(8)
done()
})
})
And I got fails :
TypeError: Cannot read property 'then' of undefined
(node:6302) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'setState' of undefined
(node:6302) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 4)
I am confused about how to test it, I will be glad about any suggestions. Thank you
You can use the async/await approach to test your function getData as it is async, but first you need to mock axios get method because you don't want include the external API call in your test, instead just mock its response:
import React from 'react';
import axios from 'axios';
import {shallow, mount} from "enzyme";
import FormWrapper from './../containers/FormWrapper/FormWrapper'
jest.mock('axios'); // for mocking axios
it('test', async () => {
const wrapper = shallow(<FormWrapper />); // I'm using shallow render, you could use mount if you want to
const instance = wrapper.instance(); // get an instance in order to call getData() directly
// mock axios get with a response
axios.get.mockImplementation(() => Promise.resolve([
{
id: 1,
val: 'val1'
},
{
id: 2,
val: 'val2'
}
]));
await instance.getData();
expect(wrapper.state().targetingData).toHaveLength(2); // this is the length of the mocked response I have added here
});
There is something I want to point out here about this function. In my opinion, the function getData() you are trying to test is actually not a good subject to testing as it doesn't contain any logic you have written.
It gets some data from API (which is not something you own), then it uses this.setState to set the state (and you don't want to test React's behavior executing setState to update your state).
In case you want to have your state populated with some data in order to use for testing other functions in the component, then I suggest you set it manually like so:
const wrapper = shallow(<FormWrapper />);
wrapper.setState({
targetingData: {...}
});
So that you concentrate more on functions and parts of code containing more logic you have written yourself.
I have a API script in a file
const ApiCall = {
fetchData: async (url) => {
const result = await fetch(url);
if (!result.ok) {
const body = await result.text(); // uncovered line
throw new Error(`Error fetching ${url}: ${result.status} ${result.statusText} - ${body}`); // uncovered line
}
return result.json();
},
};
export default ApiCall;
When I mock the call, I have two uncovered lines in code coverage.
Any idea how can I make them cover as well.
Here is what I have tried so far which is not working
it('test', async () => {
ApiCall.fetchData = jest.fn();
ApiCall.fetchData.result = { ok: false };
});
I am kind of new into Jest, so any help would be great.
You need to provide a stubb response in your test spec so that the if statement is triggered. https://www.npmjs.com/package/jest-fetch-mock will allow you to do just that. The example on their npm page should give you what you need https://www.npmjs.com/package/jest-fetch-mock#example-1---mocking-all-fetches
Basically the result is stored in state(redux) and is called from there. jest-fetch-mock overrides your api call/route and returns the stored result in redux all within the framework.
Assuming that what you want to test is the ApiCall then you would need to mock fetch. You are mocking the entire ApiCall so those lines will never execute.
Also, you have an issue, because if you find an error or promise rejection, the json() won't be available so that line will trigger an error.
Try this (haven't test it):
it('test error', (done) => {
let promise = Promise.reject(new Error("test"));
global.fetch = jest.fn(() => promise); //You might need to store the original fetch before swapping this
ApiCall.fetchData()
.catch(err => );
expect(err.message).toEqual("test");
done();
});
it('test OK', (done) => {
let promise = Promise.resolve({
json: jest.fn(() => {data: "data"})
});
global.fetch = jest.fn(() => promise);
ApiCall.fetchData()
.then(response => );
expect(response.data).toEqual("data");
done();
});
That probably won't work right away but hopefully you will get the idea. In this case, you already are working with a promise so see that I added the done() callback in the test, so you can tell jest you finished processing. There is another way to also make jest wait for the promise which is something like "return promise.then()".
Plese post back