I currently have the following custom hook to access the geolocation API:
const useGeoLocAPI = () => {
const [coords, setCoords] = useState(null);
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(
(position) => {
setCoords({
lat: position.coords.latitude,
long: position.coords.longitude,
});
},
(error) => {
console.error(error.message);
}
);
}
return [coords, setCoords];
};
export default useGeoLocAPI;
Since the success callback uses setState , i'm having a hard time trying to mock this hook for testing. I've also heard mocking setState
is an anti-pattern, which makes me wonder how I would even do this. I am able to do a simple smoke test by creating the following mock in my setupTests.js
const mockGeolocation = {
getCurrentPosition: jest.fn(),
watchPosition: jest.fn(),
};
global.navigator.geolocation = mockGeolocation;
But because this mock doesn't setState, the hook always returns null
in my tests.
Is there a way to properly mock the geolocation API so I can do the following:
- Assert the shape of the state.
- Assert it's error handling
- Assert that it can actually set state
I've created an issue of this in the project's repo in case more information is needed.
CodePudding user response:
The hook always returns null in the tests because you need to return a value in your mocked getCurrentPosition
method.
You can return a value in the mocked method by implementing a function to return the value you want.
i.e.,
test('useGeoLocAPI success', () => {
const mockGeolocation = {
getCurrentPosition: jest.fn()
.mockImplementationOnce((success) => Promise.resolve(success({
coords: {
latitude: 51.1,
longitude: 45.3
}
})))
};
global.navigator.geolocation = mockGeolocation;
const { result } = renderHook(() => useGeoLocAPI());
const [ coords ] = result.current;
expect(coords)
.toEqual({
lat: 51.1,
long: 45.3
});
});
You can also mock the geolocation getCurrentPosition
method to return an error in order to test your error logic.
test('useGeoLocAPI error handling', () => {
const mockGeolocation = {
getCurrentPosition: jest.fn()
.mockImplementationOnce((success, error) => Promise.resolve(error({ message: 'nav error' })))
};
global.navigator.geolocation = mockGeolocation;
const consoleErrorMock = jest.spyOn(global.console, 'error').mockImplementation();
const { result } = renderHook(() => useGeoLocAPI());
const [ coords ] = result.current;
expect(global.console.error)
.toHaveBeenCalledWith('nav error');
expect(coords)
.toBeNull();
consoleErrorMock.mockRestore();
});
Now that you know how to mock the geolocation getCurrentPosition method properly, you should be able to test the setState functionality of your hook.