Cannot access built-in Node.js `fetch` function from jest tests
Asked Answered
P

3

14

I have a file with a function like:

export async function loginNextAuth(displayName, password) {
    const response = await fetch('/api/auth/callback/credentials')
}

Notice there are no imports, this is a built-in Node.js fetch function in the global namespace. This works fine in Playwright tests and regular code.

For some reason both global and globalThis do not have the fetch property while running in Jest tests. This results in an error from jest saying the fetch variable is undeclared.

The process.version returned in jest tests is the same as the Node version I am using in development.

This is a similar SO question, but there OP is using an external fetch function imported from a module.

Update: Node version is v18.12.1

The error in question:

    const response = await fetch("/api/auth/callback/credentials", {
                     ^

ReferenceError: fetch is not defined
    at Object.loginNextAuth (web_app/lib/tests/jest/login.js:8:22)
    at loginAs (web_app/pages/permissions.jest.js:22:71)
Philomena answered 28/12, 2022 at 22:32 Comment(6)
…and which node.js version is it that you are using?Circumgyration
@Circumgyration v18.12.1Philomena
What is the actual error that you get?Whiten
What are you trying to achieve with this question? Testing in simple terms is; what are you testing, what do I expect the results to be. This however, only asks why won't this fetch API work in my test. But you're supposed to mock any API functions within testing and NOT send a fetch request because this; spams the endpoint, creates latency giving meaningless completion times and is unreliable for testing purposes.Rausch
@Rausch it's possible to mock endpoints with for example msw to make a realistic scenario without mocking fetch directlyWhiten
@Rausch very good points, calling fetch on login is the lesser evil, alternative being e2e login, that takes 6s. I have also used next-auth internals to login (by calling the api handler as code), but it's hackish and it's using implementation details. Calling fetch on login is the best compromise between speed and black-box-testing of logging in I can do.Philomena
C
10

When using Node 18, Jest removes the global reference to fetch.

A work around is to add it to the globals in your jest.config.js file.

globals: {
  fetch: global.fetch,
}

A couple of other potential gotchas to bare in mind:

  1. Your Node fetch will be expecting an absolute url not a relative one.
  2. fetch doesn't use Node's http module, so tools such as Nock will not currently be able to intercept the calls made. https://github.com/nock/nock/issues/2336
Curvaceous answered 9/2, 2023 at 13:53 Comment(5)
Hi, can you provide any resource about jest removing fetch from Node 18 global object? Official docs says the opposite for Jest 29 and I'm having some issues in this context. Thanks.Ferrari
Hi, not sure I know of any documentation for this. But if you try console.log(global.fetch) directly within the Node context you'll get [AsyncFunction: fetch] but run it within a Jest test context and you'll get undefined.Curvaceous
Yes, but that's exactly what it should NOT do since version 28, according to their docs: jestjs.io/blog/2022/04/25/jest-28#all-nodejs-globalsFerrari
Might be worth raising it with the Jest team if it's causing you issues @FerrariCurvaceous
this is so dumb ... Thanks for solving my issueAcephalous
F
7

For those still looking for an answer, fetch API is only available in Jest's node test environment in Jest v28+ as pointed out in this comment. To access it you must run your tests in node test environment, which is Jest's default test environment, instead of jsdom. You can either add testEnvironment: "node" to your jest config, or if you want to change it for a specific test file, you can add @jest-environment node in a docblock on top of your test file. See https://jestjs.io/docs/configuration#testenvironment-string.

Furthest answered 13/7, 2023 at 13:1 Comment(0)
A
4

You're using Node 18+ and the JSDom environment, but Jest's developers refuse to fix this issue until JSDom does.

While we wait, add this to your custom environment:

// ./jsdom-env.js
import JSDOMEnvironment from "jest-environment-jsdom";

export default class FixJSDOMEnvironment extends JSDOMEnvironment {
  constructor(...args) {
    super(...args);
    this.global.fetch = fetch;
    this.global.Request = Request;
    this.global.Response = Response;
    // And any other missing globals
  }
}

and this to your jest.config.js file:

module.exports = {
  testEnvironment: "./jsdom-env.js"
}
Adviser answered 24/2 at 5:50 Comment(1)
minor callout I needed to update to import { TestEnvironment } from "jest-environment-jsdom";Ulcerate

© 2022 - 2024 — McMap. All rights reserved.