For components that perform async operations (like API calls), use findBy* queries which return promises that resolve when the element appears. Combine this with @testing-library/jest-dom matchers and API mocking tools like Mock Service Worker (MSW).
Example:
import '@testing-library/jest-dom'
import {render, fireEvent, screen} from '@testing-library/react'
import {rest} from 'msw'
import {setupServer} from 'msw/node'
import Login from '../login'
const fakeUserResponse = {token: 'fake_user_token'}
const server = setupServer(
rest.post('/api/login', (req, res, ctx) => {
return res(ctx.json(fakeUserResponse))
}),
)
beforeAll(() => server.listen())
afterEach(() => {
server.resetHandlers()
window.localStorage.removeItem('token')
})
afterAll(() => server.close())
test('allows the user to login successfully', async () => {
render(<Login />)
fireEvent.change(screen.getByLabelText(/username/i), {
target: {value: 'chuck'},
})
fireEvent.change(screen.getByLabelText(/password/i), {
target: {value: 'norris'},
})
fireEvent.click(screen.getByText(/submit/i))
// Wait for the element to appear
const alert = await screen.findByRole('alert')
expect(alert).toHaveTextContent(/congrats/i)
expect(window.localStorage.getItem('token')).toEqual(fakeUserResponse.token)
})
Recommendation: Use Mock Service Worker (msw) to declaratively mock API communication instead of stubbing window.fetch.
const server = setupServer(
rest.post('/api/login', (req, res, ctx) => {
return res(ctx.json({token: 'fake_token'}))
}),
)
beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())
const alert = await screen.findByRole('alert')
expect(alert).toHaveTextContent(/congrats/i)
Sources: README.md