Reputation: 3528
I am trying to run Jest/Enzyme async tests for fetchTemperatures
and I've run into a bit of unfamiliar ground. If I define longitude and latitude and comment out getUserCoordinates
I am able to pass my tests. Question is, how can I write my tests to allow/mock that an async function inside of fetchTemperatures
is returning values?
~ additionally, if there are any other issues with how the code is written please let me know, I am trying to warm back up to the js environment...
fetchTemperatures.js
export const fetchTemperatures = async (format = 'weather', source = 'openWeather') => {
// for 5-day-forecast set format arg to "forecast"
try {
let response;
const { longitude, latitude } = await getUserCoordinates();
//! the nested async above is preventing me from running the tests...
// let longitude = 100;
// let latitude = 100;
if (source === 'openWeather') {
response = await fetch(
`${openWeatherURL}/${format}?APPID=${process.env
.REACT_APP_OW_API_KEY}&lat=${latitude}&lon=${longitude}&units=imperial`
);
} else if (source === 'darkSky') {
response = await fetch(
`${darkSkyURL}${process.env
.REACT_APP_DS_API_KEY}/${latitude},${longitude}?exclude=[currently,minutely,hourly,flags]`
);
} else {
//! is this getting hit? -- not sure
throw new Error("Enter a valid source string. Ex: 'openWeather' or 'darkSky'");
}
return await response.json();
} catch (error) {
throw new Error('Fetch failed');
}
};
fetchTemperatures.test.js
describe('fetchTemperatures()', () => {
let mockResponse = { data: 'Weather related data.' };
let response;
beforeAll(() => {
window.fetch = jest.fn().mockImplementation(() => {
return Promise.resolve({
ok: true,
json: () => Promise.resolve(mockResponse)
});
});
//! Number of calls: 0 === FIGURE OUT NESTED ASYNC IN SOURCE
response = fetchTemperatures();
});
// argument-based routes
it('args=default, should call fetch with the correct url', () => {
fetchTemperatures();
expect(window.fetch).toHaveBeenCalledWith(
`${openWeatherURL}/weather?APPID=${process.env
.REACT_APP_OW_API_KEY}&lat=${latitude}&lon=${longitude}&units=imperial`
);
});
it('args="weather", should call fetch with the correct url', () => {
fetchTemperatures('forecast');
expect(window.fetch).toHaveBeenCalledWith(
`${openWeatherURL}/weather?APPID=${process.env
.REACT_APP_OW_API_KEY}&lat=${latitude}&lon=${longitude}&units=imperial`
);
});
it('args="weather", "darkSky", should call fetch with the correct url', () => {
fetchTemperatures('weather', 'darkSky');
expect(window.fetch).toHaveBeenCalledWith(
`${darkSkyURL}${process.env
.REACT_APP_DS_API_KEY}/${latitude},${longitude}?exclude=[currently,minutely,hourly,flags]`
);
});
// success
it('Success::Promise Resolve: should return with the current weather', () => {
response.then((results) => {
expect(results).toEqual(mockResponse);
});
});
// failure - success but response not ok
it('Failure::Response !ok: should return an error', () => {
window.fetch = jest.fn().mockImplementation(() => {
return Promise.resolve({
ok: false
});
});
expect(fetchTemperatures()).rejects.toEqual(Error('Fetch failed'));
});
// failure - reject
it('Failure::Promise Rejects', () => {
window.fetch = jest.fn().mockImplementation(() => {
return Promise.reject(Error('Fetch failed'));
});
expect(fetchTemperatures()).rejects.toEqual(Error('Fetch failed'));
});
});
Upvotes: 0
Views: 2327
Reputation: 6539
It's pretty straightforward to mock getUserCoordinates
, you have a few different options:
jest.mock
, which takes the path to the module containing getUserCoordinates
and intercepts it, returning the mocked value from the callback you provide:// This assumes `getUserCoordinates` is a named export.
// If it's the default export just have the callback return jest.fn()...
jest.mock('./get-user-coordinates.js', () => ({
getUserCoordinates: jest.fn().mockResolvedValue({lat: 0, long: 0});
}));
import * as
, which allows you to mock imports without jest.mock
:// You can name `api` whatever you want
import * as api from './get-user-coordinates';
api.getUserCoordinates = jest.fn().mockResolvedValue({lat: 0, long: 0});
One other thing: In your third-to-last test, you should make sure to return response.then()...
, otherwise your test will pass before waiting to enter the .then()
block:
// Returning the promise makes the test wait until the
// `.then` is executed before finishing.
return response.then((results) => {
expect(results).toEqual(mockResponse);
});
Upvotes: 1