How to really call fetch in Jest test
Asked Answered
N

1

17

Is there a way to call fetch in a Jest test? I just want to call the live API to make sure it is still working. If there are 500 errors or the data is not what I expect than the test should report that.

I noticed that using request from the http module doesn't work. Calling fetch, like I normally do in the code that is not for testing, will give an error: Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout. The API returns in less than a second when I call it in the browser. I use approximately the following to conduct the test but I also have simply returned the fetch function from within the test without using done with a similar lack of success:

import { JestEnvironment } from "@jest/environment";
import 'isomorphic-fetch';
import { request, } from "http";
jest.mock('../MY-API');

describe('tests of score structuring and display', () => {
    test('call API - happy path', (done) => {
        fetch(API).then(
            res => res.json()
        ).then(res => {
            expect(Array.isArray(response)).toBe(true);
            console.log(`success: ${success}`);
            done();
        }).catch(reason => {
            console.log(`reason: ${reason}`);
            expect(reason).not.toBeTruthy();
            done();
        });
    });
});

Oddly, there is an error message I can see as a console message after the timeout is reached: reason: ReferenceError: XMLHttpRequest is not defined

How can I make an actual, not a mocked, call to a live API in a Jest test? Is that simply prohibited? I don't see why this would fail given the documentation so I suspect there is something that is implicitly imported in React-Native that must be explicitly imported in a Jest test to make the fetch or request function work.

Niggling answered 3/4, 2019 at 15:19 Comment(6)
I think your approach is incorrect. In your unit tests you should not need to check if the service is alive, you should mock the call and test your handlers for success/failure, you do not need to perform a real API call to achieve thatAlisun
@Jony-Y, suppose I was crazy enough to want to check if the service is alive. Is that even allowed?Niggling
In theory everything is allowed but it doesnt sound like it should be in your testing scope. you need to test your logic and how you handle changes. Not of services or 3rd party packages, they have their own tests.Alisun
@Jony-Y, I don't strongly disagree. I'm trying to do test something that is not normally considered in the proper testing scope for the mobile app. How can I do it, nonetheless?Niggling
@Alisun there is nothing wrong with integration/functional tests. I agree that unit tests should be isolated, but there is a time and place for functional tests. That being said in order to use WebAPIs you need to run your tests in a browser environment. Alternatively you could try assigning a fetch polyfill (like node-fetch) to the global object before testing begins.Princeling
Let's separate functional testing and integration testing. If you want to test e2e that's one thing, if you want to test your code, that's another. There is no reason to check a 3rd party code in order to test your code. Yes you can find libraries that will allow it. But it's not something that you need to do in your unit tests. E2E of course is another thing. Again. You need to make sure you test your own code and separate your concerns with your testsAlisun
P
21

Putting aside any discussion about whether making actual network calls in unit tests is best practice...

There's no reason why you couldn't do it.

Here is a simple working example that pulls data from JSONPlaceholder:

import 'isomorphic-fetch';

test('real fetch call', async () => {
  const res = await fetch('https://jsonplaceholder.typicode.com/users/1');
  const result = await res.json();
  expect(result.name).toBe('Leanne Graham');  // Success!
});

With all the work Jest does behind the scenes (defines globals like describe, beforeAll, test, etc., routes code files to transpilers, handles module caching and mocking, etc.) ultimately the actual tests are just JavaScript code and Jest just runs whatever JavaScript code it finds, so there really aren't any limitations on what you can run within your unit tests.

Profusive answered 4/4, 2019 at 1:30 Comment(5)
For anyone looking to call code that itself uses fetch, I found the advice helpful to mock the fetch function itself: global.fetch = fetch where fetch is your imported fetch function.Niggling
Aside from tests taking longer to run, what really is the problem with not mocking fetch? Isn't it best that tests reflect real-world conditions as closely as possible? Hitting a remote API - okay, that may take a while... but running an API locally and fetching against it? What's really the problem with this? "Bad practice?"Partheniaparthenocarpy
@Niggling what if fetch is not imported but rather the built-in Node.js fetch function?Klee
@Partheniaparthenocarpy there are 2 kinds of tests. integration and functional tests. functional tests should be isolated and extensive, handling all possible permutations of inputs and edge cases. integrations tests cannot do this... it's too slow. so they just need to check that the major integration assumptions made by the functional tests are correct.Subtonic
@Partheniaparthenocarpy Sometimes mocking complex relational data is the bulk of the work, and it's harder without a DB and API to back you up. Plus, since those are already built, there isn't as much of a benefit to mocking fetch and ALL backend data models... guaranteed to find data issues because the frontend would be looking at the data models a lot closer than anyone ever has on the backend... hence, for me, our first jest tests in 400k line code base, made of a Frankenstein of antique frameworks... I need tests, not models... we can't even use typescript yet, so model value is low and time high.Fundament

© 2022 - 2024 — McMap. All rights reserved.