Kalimantan
Kalimantan

Reputation: 771

Fetch never returns data when msw (Mock Service Worker) intercepts request

I am currently using next/jest to setup my environment, with msw and @testing@library/react. My test looks like:

import { TestComponent } from '../../../components/TestComponent'
import React from 'react'
import { render } from '@testing-library/react'

test('Test TestComponent', function () {
  const wrap = render(<TestComponent />)
  expect(wrap.container.childElementCount).toBe(2)
})

My jest.config.js looks like:

const nextJest = require('next/jest')

const createJestConfig = nextJest({
  dir: './',
})

const customJestConfig = {
  setupFilesAfterEnv: ['<rootDir>/test_utils/setup-env.js'],
  moduleDirectories: ['node_modules', '<rootDir>/'],
  testEnvironment: 'jest-environment-jsdom',
}

module.exports = createJestConfig(customJestConfig)

My setup-env.js looks like this:

import '@testing-library/jest-dom'
import { server } from './server.js'

beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

My component looks something like this...

import React, { useEffect, useState } from 'react'

import { useSWRFetch } from '../../../hooks'

export const TestComponent = (): JSX.Element => {
  const [data, setData] = useState([])
  const dataInfo = useSWRFetch({
    endpoint: `${process.env.NEXT_PUBLIC_API_URL}/v1/endpoint`,
  })

  useEffect(() => {
    if (dataInfo) {
      setDomains(dataInfo.data)
    }
  }, [dataInfo])
  return (
    <>
        {data &&
          data.map((item, idx) => (
                  <div>
                    <p>{item.count | 0}</p>
                    <p>Job</p>
                  </div>
          ))}
    </>
  )
}

When I run my test however I get the following error...

ReferenceError: fetch is not defined

However when I value of dataInfo.data it is always undefined.

I have a fetcher function which SWR uses that looks like the following:

async function fetcher(...args) {
  console.log("BEFORE REQUEST")
  const url = args[0]
  const token = args[1]
  const method = args.length > 2 ? args[2] : 'GET'
  const body = args.length > 3 ? { body: JSON.stringify(args[3]) } : {}
  const res = await fetch(url, {
    method: method,

    headers:
      method != 'GET'
        ? {
            Authorization: `bearer ${token}`,
            'Content-Type': 'application/json',
          }
        : {
            Authorization: `Bearer ${token}`,
          },
    ...body,
  })

  console.log("AFTER REQUEST")

  if (!res.ok) {
    const error = new Error('An error occurred while fetching the data.')
    // Attach extra info to the error object.
    error.info = await res.json()
    error.status = res.status
    throw error
  }

  return res.json()
}

function useSWRFetch({ endpoint, token, options = null, condition=true }) {
  const { data, error, mutate } = useSWR(token && condition ? [endpoint, token] : null, fetcher, options)

  return {
    mutate: mutate,
    data: data,
    isLoading: !error && !data,
    error: error,
  }
}

However it never reaches the second console, AFTER REQUEST when the request is intercepted.

Upvotes: 4

Views: 11439

Answers (2)

bastianwegge
bastianwegge

Reputation: 2498

Taken from React Jest tests failing with MSW the answer is probably the same:

You need to make sure that your tests are waiting for things to unwrap. If you're not awaiting something to happen, your test-suite will not execute properly.

Try implementing a loading-state and then:

await waitForElementToBeRemoved(screen.getByText("Loading ..."))

As this might be considered "bad practice" you can also do the opposite which would be waiting for something to appear:

await screen.findByText('The text you are waiting for');

I'd also like to point out that @kettanaito 's answer helped me a lot to find a good polyfill for vitest. Thanks!

Upvotes: 3

kettanaito
kettanaito

Reputation: 1574

As per the error message, you haven't polyfilled fetch in your tests. Node.js doesn't have the fetch function as it's a browser-only API. If you're testing code that uses fetch (your fetcher does), you need to polyfill that function always. This is often done by frameworks like Create React App, but if you're using a custom setup you have to do that manually.

I recommend using whatwg-fetch:

// setup-env.js
import 'whatwg-fetch'

// ...the rest of your test setup

Once fetch is polyfilled, Jest will be able to run your tests and you will get the mocked response back.

Upvotes: 3

Related Questions