Aller au contenu principal

Exemple

[Traduction Bêta Non Officielle]

Cette page a été traduite par PageTurner AI (bêta). Non approuvée officiellement par le projet. Vous avez trouvé une erreur ? Signaler un problème →

Démarrage rapide

Voici une configuration minimale pour vous lancer. Pour une explication détaillée de chaque ligne, descendez jusqu'à la version annotée. Pour voir une configuration de test plus avancée, consultez l'Exemple complet.

import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import '@testing-library/jest-dom'
import Fetch from './fetch'

test('loads and displays greeting', async () => {
// ARRANGE
render(<Fetch url="/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.
import userEvent from '@testing-library/user-event'
// add custom jest matchers from jest-dom
import '@testing-library/jest-dom'
// the component to test
import Fetch from './fetch'

test('loads and displays greeting', async () => {
// Render a React element into the DOM
render(<Fetch url="/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()
})

Exemple complet

Consultez les sections suivantes pour une analyse détaillée du test

[]

Nous recommandons d'utiliser la bibliothèque Mock Service Worker (MSW) pour simuler déclarativement les communications API dans vos tests, plutôt que de créer des stub pour window.fetch ou de dépendre d'adaptateurs tiers.

[]

Notre exemple utilise axios pour les appels API. Si votre application utilise fetch(), notez que JSDOM ne l'inclut pas par défaut. Avec vitest comme runner de tests, il sera inclus automatiquement. Pour jest, vous devrez peut-être polyfill fetch() manuellement ou utiliser l'environnement jest-fixed-jsdom qui inclut fetch.

__tests__/fetch.test.jsx
import React from 'react'
import {http, HttpResponse} from 'msw'
import {setupServer} from 'msw/node'
import {render, fireEvent, screen} from '@testing-library/react'
import '@testing-library/jest-dom'
import Fetch from '../fetch'

const server = setupServer(
http.get('/greeting', () => {
return HttpResponse.json({greeting: 'hello there'})
}),
)

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

test('loads and displays greeting', async () => {
render(<Fetch url="/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', () => {
return new HttpResponse(null, {status: 500})
}),
)

render(<Fetch url="/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()
})

Pas à pas

Imports

// import dependencies
import React from '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
import Fetch from '../fetch'
test('loads and displays greeting', async () => {
// Arrange
// Act
// Assert
})

Mock

Utilisez la fonction setupServer de msw pour simuler une requête API effectuée par notre composant testé.

// 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
return HttpResponse.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) => {
return new HttpResponse(null, {status: 500})
}),
)

// ...
})

Préparation (Arrange)

La méthode render affiche un élément React dans le DOM.

render(<Fetch url="/greeting" />)

Action (Act)

La méthode fireEvent permet de déclencher des événements pour simuler des actions utilisateur.

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')

Vérification (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()

Système sous test

fetch.jsx
import React, {useState, useReducer} from 'react'
import axios from 'axios'

const initialState = {
error: null,
greeting: null,
}

function greetingReducer(state, action) {
switch (action.type) {
case 'SUCCESS': {
return {
error: null,
greeting: action.greeting,
}
}
case 'ERROR': {
return {
error: action.error,
greeting: null,
}
}
default: {
return state
}
}
}

export default function Fetch({url}) {
const [{error, greeting}, dispatch] = useReducer(
greetingReducer,
initialState,
)
const [buttonClicked, setButtonClicked] = useState(false)

const fetchGreeting = async url =>
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>
<button onClick={() => fetchGreeting(url)} disabled={buttonClicked}>
{buttonText}
</button>
{greeting && <h1>{greeting}</h1>}
{error && <p role="alert">Oops, failed to fetch!</p>}
</div>
)
}