Example
Quickstart
This is a minimal setup to get you started. If you want to see a description ofwhat each line does, scroll down to theannotated version. Scroll down toFull Example to see a more advanced test setup.
import{render, screen}from'@testing-library/react'
importuserEventfrom'@testing-library/user-event'
import'@testing-library/jest-dom'
importFetchfrom'./fetch'
test('loads and displays greeting',async()=>{
// ARRANGE
render(<Fetchurl="/greeting"/>)
// ACT
await userEvent.click(screen.getByText('Load Greeting'))
await screen.findByRole('heading')
// ASSERT
expect(screen.getByRole('heading')).toHaveTextContent('hello there')
expect(screen.getByRole('button')).toBeDisabled()
})
Quickstart (Annotated Example)
// import react-testing methods
import{render, screen}from'@testing-library/react'
// userEvent library simulates user interactions by dispatching the events that would happen if the interaction took place in a browser.
importuserEventfrom'@testing-library/user-event'
// add custom jest matchers from jest-dom
import'@testing-library/jest-dom'
// the component to test
importFetchfrom'./fetch'
test('loads and displays greeting',async()=>{
// Render a React element into the DOM
render(<Fetchurl="/greeting"/>)
await userEvent.click(screen.getByText('Load Greeting'))
// wait before throwing an error if it cannot find an element
await screen.findByRole('heading')
// assert that the alert message is correct using
// toHaveTextContent, a custom matcher from jest-dom.
expect(screen.getByRole('heading')).toHaveTextContent('hello there')
expect(screen.getByRole('button')).toBeDisabled()
})
Full Example
See the following sections for a detailed breakdown of the test
We recommend using theMock Service Worker (MSW)library to declaratively mock API communication in your tests instead ofstubbingwindow.fetch
, or relying on third-party adapters.
Our example here uses axios to make its API calls. If your application usesfetch()
tomake its API calls, then be aware that by default JSDOM does not include fetch.If you are using vitest as your test runner, it will be included for you. Forjest you may wish to manually polyfillfetch()
or use thejest-fixed-jsdom environment whichincludes fetch.
importReactfrom'react'
import{http,HttpResponse}from'msw'
import{setupServer}from'msw/node'
import{render, fireEvent, screen}from'@testing-library/react'
import'@testing-library/jest-dom'
importFetchfrom'../fetch'
const server=setupServer(
http.get('/greeting',()=>{
returnHttpResponse.json({greeting:'hello there'})
}),
)
beforeAll(()=> server.listen())
afterEach(()=> server.resetHandlers())
afterAll(()=> server.close())
test('loads and displays greeting',async()=>{
render(<Fetchurl="/greeting"/>)
fireEvent.click(screen.getByText('Load Greeting'))
await screen.findByRole('heading')
expect(screen.getByRole('heading')).toHaveTextContent('hello there')
expect(screen.getByRole('button')).toBeDisabled()
})
test('handles server error',async()=>{
server.use(
http.get('/greeting',()=>{
returnnewHttpResponse(null,{status:500})
}),
)
render(<Fetchurl="/greeting"/>)
fireEvent.click(screen.getByText('Load Greeting'))
await screen.findByRole('alert')
expect(screen.getByRole('alert')).toHaveTextContent('Oops, failed to fetch!')
expect(screen.getByRole('button')).not.toBeDisabled()
})
Step-By-Step
Imports
// import dependencies
importReactfrom'react'
// import API mocking utilities from Mock Service Worker
import{http,HttpResponse}from'msw'
import{setupServer}from'msw/node'
// import react-testing methods
import{render, fireEvent, screen}from'@testing-library/react'
// add custom jest matchers from jest-dom
import'@testing-library/jest-dom'
// the component to test
importFetchfrom'../fetch'
test('loads and displays greeting',async()=>{
// Arrange
// Act
// Assert
})
Mock
Use thesetupServer
function frommsw
to mock an API request that our testedcomponent makes.
// declare which API requests to mock
const server=setupServer(
// capture "GET /greeting" requests
http.get('/greeting',(req, res, ctx)=>{
// respond using a mocked JSON body
returnHttpResponse.json({greeting:'hello there'})
}),
)
// establish API mocking before all tests
beforeAll(()=> server.listen())
// reset any request handlers that are declared as a part of our tests
// (i.e. for testing one-time error scenarios)
afterEach(()=> server.resetHandlers())
// clean up once the tests are done
afterAll(()=> server.close())
// ...
test('handles server error',async()=>{
server.use(
// override the initial "GET /greeting" request handler
// to return a 500 Server Error
http.get('/greeting',(req, res, ctx)=>{
returnnewHttpResponse(null,{status:500})
}),
)
// ...
})
Arrange
Therender
method renders a React element into the DOM.
render(<Fetchurl="/greeting"/>)
Act
ThefireEvent
method allows you to fireevents to simulate user actions.
fireEvent.click(screen.getByText('Load Greeting'))
// wait until the `get` request promise resolves and
// the component calls setState and re-renders,
// throwing an error if it cannot find an element
await screen.findByRole('heading')
Assert
// assert that the alert message is correct using
// toHaveTextContent, a custom matcher from jest-dom.
expect(screen.getByRole('alert')).toHaveTextContent('Oops, failed to fetch!')
// assert that the button is not disabled using
// toBeDisabled, a custom matcher from jest-dom.
expect(screen.getByRole('button')).not.toBeDisabled()
System Under Test
importReact,{useState, useReducer}from'react'
importaxiosfrom'axios'
const initialState={
error:null,
greeting:null,
}
functiongreetingReducer(state, action){
switch(action.type){
case'SUCCESS':{
return{
error:null,
greeting: action.greeting,
}
}
case'ERROR':{
return{
error: action.error,
greeting:null,
}
}
default:{
return state
}
}
}
exportdefaultfunctionFetch({url}){
const[{error, greeting}, dispatch]=useReducer(
greetingReducer,
initialState,
)
const[buttonClicked, setButtonClicked]=useState(false)
constfetchGreeting=asyncurl=>
axios
.get(url)
.then(response=>{
const{data}= response
const{greeting}= data
dispatch({type:'SUCCESS', greeting})
setButtonClicked(true)
})
.catch(error=>{
dispatch({type:'ERROR', error})
})
const buttonText= buttonClicked?'Ok':'Load Greeting'
return(
<div>
<buttononClick={()=>fetchGreeting(url)}disabled={buttonClicked}>
{buttonText}
</button>
{greeting&&<h1>{greeting}</h1>}
{error&&<prole="alert">Oops, failed to fetch!</p>}
</div>
)
}