Testing async code with Jest

After a little bit of struggle, I have managed to implement a jest that correctly tests an async fetch call. That is, suppose clicking a button initiates a POST request, and upon success some action takes place or some message gets displayed. I needed a test to assert that the action indeed took place.

My component looks as follows:

const ResultsModal = (props) => {
  const {
    results, setResults,
  } = useContext(ResultsCtx)
  
  const handleSave = () => {
    fetch(url, {}).then(r => r.json()).then(inns => {
      setResults([])
    })
  })
  return <h1>
    some rendered content, a button to click
  </h1>
 }

I'm obviously omiting a lot for brevity. What I want to show is how to write the test. As you can see, the handleSave is an async method that changes state. The test has to wait for that to happen, and somehow tap into state to be able to view it.

As it happens, the state is provided by a provider. In test, I replace the provider entirely with a mock. That mock uses jest.spy, and is accessible in test:

import { fireEvent, render, screen, waitFor } from '@testing-library/react'

beforeEach(() => {
  jest.spyOn(global, 'fetch').mockImplementation(async (url, params) => {
    return Promise.resolve({ json: () => [] })
  })
})

afterEach(() => {
  jest.restoreAllMocks()
})

test('ResultsModal test', async () => {
  const setResults = jest.fn()
  render(<Router>
    <ResultsCtx.Provider value={{
      setResults,
    }} >
      <ResultsModal />
    </ResultsCtx.Provider>
  </Router>)
  const btn = screen.getByText("Save")
  await fireEvent.click(btn)
  await new Promise(process.nextTick)
  
  expect(setResults).toHaveBeenCalledTimes(1)
  expect(setResults).toHaveBeenCalledWith([])
  
})

So my test both (1) waits for an async call to finish executing and (2) asserts a state change. As intended.

^__^

Please login or register to post a comment.