Testiranje se često doživljava kao dosadan proces. To je dodatni kôd koji morate napisati, a u nekim slučajevima, da budem iskren, nije potreban. Ali svaki programer trebao bi znati barem osnove testiranja. Povećava povjerenje u proizvode koje grade, a većini tvrtki to je uvjet.
U svijetu React-a postoji nevjerojatna biblioteka pod nazivom the react-testing-library
koja vam pomaže da učinkovitije testirate svoje aplikacije React. Koristite ga s Jestom.
U ovom ćemo članku vidjeti 8 jednostavnih koraka koje možete započeti kako biste započeli testiranje svojih React aplikacija poput šefa.
- Preduvjeti
- Osnove
- Što je React Testing Library?
- 1. Kako stvoriti probni snimak?
- 2. Testiranje DOM elemenata
- 3. Ispitni događaji
- 4. Testiranje asinkronih radnji
- 5. Ispitivanje React Redux
- 6. Testiranje konteksta reakcije
- 7. Ispitivanje React usmjerivača
- 8. Testiranje HTTP zahtjeva
- Završne misli
- Sljedeći koraci
Preduvjeti
Ovaj tutorial pretpostavlja da imate barem osnovno razumijevanje React-a. Usredotočit ću se samo na dio testiranja.
A da biste nastavili dalje, morate klonirati projekt izvođenjem na vašem terminalu:
git clone //github.com/ibrahima92/prep-react-testing-library-guide
Zatim pokrenite:
yarn
Ili, ako koristite NPM:
npm install
I to je to! Sada zaronimo u neke osnove.
Osnove
U ovom će se članku puno koristiti neke ključne stvari, a razumijevanje njihove uloge može vam pomoći u razumijevanju.
it or test
: opisuje sam test. Kao parametre uzima se naziv testa i funkcija koja drži testove.
expect
: uvjet koji test mora proći. Primit će parametar s usporedbom.
a matcher
: funkcija koja se primjenjuje na očekivano stanje.
render
: metoda korištena za generiranje zadane komponente.
import React from 'react' import {render} from '@testing-library/react' import App from './App' it('should take a snapshot', () => { const { asFragment } = render() expect(asFragment()).toMatchSnapshot() }) });
Kao što vidite, opisujemo test pomoću it
, a zatim, pomoću kojeg render
prikazujemo komponentu aplikacije i očekujemo da se asFragment()
podudara toMatchSnapshot()
(podudaranje koje pruža jest-dom).
Inače, render
metoda vraća nekoliko metoda koje možemo koristiti za testiranje naših značajki. Za dobivanje metode koristili smo i destrukturiranje.
S tim u vezi, krenimo dalje i u sljedećem odjeljku saznajmo više o knjižnici za reakcijsko testiranje.
Što je knjižnica za testiranje reakcija?
Knjižnica za reakcijsko testiranje vrlo je lagan paket koji je stvorio Kent C. Dodds. Zamjena je za Enzim i pruža lagane uslužne funkcije povrh react-dom
i react-dom/test-utils
.
Knjižnica za reakcijsko testiranje je knjižnica za testiranje DOM-a, što znači da, umjesto da se bavi primjercima prikazanih komponenti React, ona obrađuje DOM elemente i njihovo ponašanje pred stvarnim korisnicima.
Izvrsna je knjižnica, (relativno je lako započeti s korištenjem) i potiče dobre prakse testiranja. Napomena - možete ga koristiti i bez Jest-a.
"Što više vaši testovi podsjećaju na način na koji se koristi vaš softver, to vam mogu pružiti više povjerenja."
Počnimo ga koristiti u sljedećem odjeljku. Usput, ne morate instalirati nijedan paket, jer create-react-app
dolazi s knjižnicom i njezinim ovisnostima.
1. Kako stvoriti testni snimak
Snimka, kao što i samo ime govori, omogućuje nam spremanje snimke dane komponente. Puno pomaže kada ažurirate ili izvršite neke promjene, a želite dobiti ili usporediti promjene.
Sada, napravimo snimak App.js
datoteke.
App.test.js
import React from 'react' import {render, cleanup} from '@testing-library/react' import App from './App' afterEach(cleanup) it('should take a snapshot', () => { const { asFragment } = render() expect(asFragment()).toMatchSnapshot() }) });
Da bismo napravili snimku, prvo moramo uvesti render
i cleanup
. Ove će se dvije metode puno koristiti u ovom članku.
render
, kao što pretpostavljate, pomaže u prikazivanju komponente React. I cleanup
prenosi se kao parametar da afterEach
se samo očisti sve nakon svakog testa kako bi se izbjeglo curenje memorije.
Dalje, komponentu aplikacije možemo prikazati pomoću render
i vratiti se asFragment
kao vraćena vrijednost iz metode. I na kraju, pobrinite se da se fragment komponente aplikacije podudara sa snimkom.
Sada, da biste pokrenuli test, otvorite terminal i idite do korijena projekta i pokrenite sljedeću naredbu:
yarn test
Ili, ako koristite npm:
npm test
As a result, it will create a new folder __snapshots__
and a file App.test.js.snap
in the src
which will look like this:
App.test.js.snap
// Jest Snapshot v1, //goo.gl/fbAQLP exports[`Take a snapshot should take a snapshot 1`] = ` Testing
`;
And if you make another change in App.js
, the test will fail, because the snapshot will no longer match the condition. To make it passes, just press u
to update it. And you'll have the updated snapshot in App.test.js.snap
.
Now, let's move on and start testing our elements.
2. Testing DOM elements
To test our DOM elements, we first have to look at the TestElements.js
file.
TestElements.js
import React from 'react' const TestElements = () => { const [counter, setCounter] = React.useState(0) return ( { counter }
setCounter(counter + 1)}> Up setCounter(counter - 1)}>Down ) } export default TestElements
Here, the only thing you have to retain is data-testid
. It will be used to select these elements from the test file. Now, let's write the unit test:
Test if the counter is equal to 0:
TestElements.test.js
import React from 'react'; import { render, cleanup } from '@testing-library/react'; import TestElements from './TestElements' afterEach(cleanup); it('should equal to 0', () => { const { getByTestId } = render(); expect(getByTestId('counter')).toHaveTextContent(0) });
Kao što vidite, sintaksa je prilično slična prethodnom testu. Jedina je razlika u tome što koristimo getByTestId
za odabir potrebnih elemenata (sjetimo se data-testid
) i provjeru je li prošao test. Drugim riječima, provjeravamo je li sadržaj teksta
{ counter }
jednako je 0.Testirajte jesu li tipke omogućene ili onemogućene:
TestElements.test.js
(dodajte sljedeći blok koda u datoteku)
it('should be enabled', () => { const { getByTestId } = render(); expect(getByTestId('button-up')).not.toHaveAttribute('disabled') }); it('should be disabled', () => { const { getByTestId } = render(); expect(getByTestId('button-down')).toBeDisabled() });
Ovdje, kao i obično, koristimo getByTestId
za odabir elemenata i provjeru za prvi test ima li gumb disabled
atribut. A za drugo, ako je gumb onemogućen ili ne.
A ako datoteku spremite ili ponovo pokrenete na terminalu yarn test
, test će proći.
Čestitamo! Vaš prvi test je prošao!

Sada, naučimo kako testirati događaj u sljedećem odjeljku.
3. Ispitni događaji
Before writing our unit tests, let's first check what the TestEvents.js
looks like.
TestEvents.js
import React from 'react' const TestEvents = () => { const [counter, setCounter] = React.useState(0) return ( { counter }
setCounter(counter + 1)}> Up setCounter(counter - 1)}>Down ) } export default TestEvents
Now, let's write the tests.
Test if the counter increments and decrements correctly when we click on buttons:
TestEvents.test.js
import React from 'react'; import { render, cleanup, fireEvent } from '@testing-library/react'; import TestEvents from './TestEvents' afterEach(cleanup); it('increments counter', () => { const { getByTestId } = render(); fireEvent.click(getByTestId('button-up')) expect(getByTestId('counter')).toHaveTextContent('1') }); it('decrements counter', () => { const { getByTestId } = render(); fireEvent.click(getByTestId('button-down')) expect(getByTestId('counter')).toHaveTextContent('-1') });
As you can see, these two tests are very similar except the expected text content.
The first test fires a click event with fireEvent.click()
to check if the counter increments to 1 when the button is clicked.
And the second one checks if the counter decrements to -1 when the button is clicked.
fireEvent
has several methods you can use to test events, so feel free to dive into the documentation to learn more.
Now that we know how to test events, let's move on and learn in the next section how to deal with asynchronous actions.
4. Testing asynchronous actions
An asynchronous action is something that can take time to complete. It can be an HTTP request, a timer, and so on.
Now, let's check the TestAsync.js
file.
TestAsync.js
import React from 'react' const TestAsync = () => { const [counter, setCounter] = React.useState(0) const delayCount = () => ( setTimeout(() => { setCounter(counter + 1) }, 500) ) return ( { counter }
Up setCounter(counter - 1)}>Down ) } export default TestAsync
Here, we use setTimeout()
to delay the incrementing event by 0.5s.
Test if the counter is incremented after 0.5s:
TestAsync.test.js
import React from 'react'; import { render, cleanup, fireEvent, waitForElement } from '@testing-library/react'; import TestAsync from './TestAsync' afterEach(cleanup); it('increments counter after 0.5s', async () => { const { getByTestId, getByText } = render(); fireEvent.click(getByTestId('button-up')) const counter = await waitForElement(() => getByText('1')) expect(counter).toHaveTextContent('1') });
To test the incrementing event, we first have to use async/await to handle the action because, as I said earlier, it takes time to complete.
Next, we use a new helper method getByText()
. This is similar to getByTestId()
, except that getByText()
selects the text content instead of id or data-testid.
Now, after clicking to the button, we wait for the counter to be incremented with waitForElement(() => getByText('1'))
. And once the counter incremented to 1, we can now move to the condition and check if the counter is effectively equal to 1.
That being said, let's now move to more complex test cases.
Are you ready?

5. Testing React Redux
If you're new to React Redux, this article might help you. Otherwise, let's check what the TestRedux.js
looks like.
TestRedux.js
import React from 'react' import { connect } from 'react-redux' const TestRedux = ({counter, dispatch}) => { const increment = () => dispatch({ type: 'INCREMENT' }) const decrement = () => dispatch({ type: 'DECREMENT' }) return ( { counter }
Up Down ) } export default connect(state => ({ counter: state.count }))(TestRedux)
And for the reducer:
store/reducer.js
export const initialState = { count: 0, } export function reducer(state = initialState, action) { switch (action.type) { case 'INCREMENT': return { count: state.count + 1, } case 'DECREMENT': return { count: state.count - 1, } default: return state } }
As you can see, there is nothing fancy – it's just a basic Counter Component handled by React Redux.
Now, let's write the unit tests.
Test if the initial state is equal to 0:
TestRedux.test.js
import React from 'react' import { createStore } from 'redux' import { Provider } from 'react-redux' import { render, cleanup, fireEvent } from '@testing-library/react'; import { initialState, reducer } from '../store/reducer' import TestRedux from './TestRedux' const renderWithRedux = ( component, { initialState, store = createStore(reducer, initialState) } = {} ) => { return { ...render({component}), store, } } afterEach(cleanup); it('checks initial state is equal to 0', () => { const { getByTestId } = renderWithRedux() expect(getByTestId('counter')).toHaveTextContent('0') })
There are a couple of things we need to import to test React Redux. And here, we create our own helper function renderWithRedux()
to render the component since it will be used several times.
renderWithRedux()
receives as parameters the component to render, the initial state, and the store. If there is no store, it will create a new one, and if it doesn't receive an initial state or a store, it returns an empty object.
Next, we use render()
to render the component and pass the store to the Provider.
That being said, we can now pass the component TestRedux
to renderWithRedux()
to test if the counter is equal to 0
.
Test if the counter increments and decrements correctly:
TestRedux.test.js
(add the following code block to the file)
it('increments the counter through redux', () => { const { getByTestId } = renderWithRedux(, {initialState: {count: 5} }) fireEvent.click(getByTestId('button-up')) expect(getByTestId('counter')).toHaveTextContent('6') }) it('decrements the counter through redux', () => { const { getByTestId} = renderWithRedux(, { initialState: { count: 100 }, }) fireEvent.click(getByTestId('button-down')) expect(getByTestId('counter')).toHaveTextContent('99') })
To test the incrementing and decrementing events, we pass an initial state as a second argument to renderWithRedux()
. Now, we can click on the buttons and test if the expected result matches the condition or not.
Now, let's move to the next section and introduce React Context.
React Router and Axios will come next – are you still with me?

6. Testing React Context
If you're new to React Context, check out this article first. Otherwise, let's check the TextContext.js
file.
TextContext.js
import React from "react" export const CounterContext = React.createContext() const CounterProvider = () => { const [counter, setCounter] = React.useState(0) const increment = () => setCounter(counter + 1) const decrement = () => setCounter(counter - 1) return ( ) } export const Counter = () => { const { counter, increment, decrement } = React.useContext(CounterContext) return ( { counter }
Up Down ) } export default CounterProvider
Now, the counter state is managed through React Context. Let's write the unit test to check if it behaves as expected.
Test if the initial state is equal to 0:
TextContext.test.js
import React from 'react' import { render, cleanup, fireEvent } from '@testing-library/react' import CounterProvider, { CounterContext, Counter } from './TestContext' const renderWithContext = ( component) => { return { ...render( {component} ) } } afterEach(cleanup); it('checks if initial state is equal to 0', () => { const { getByTestId } = renderWithContext() expect(getByTestId('counter')).toHaveTextContent('0') })
As in the previous section with React Redux, here we use the same approach, by creating a helper function renderWithContext()
to render the component. But this time, it receives only the component as a parameter. And to create a new context, we pass CounterContext
to the Provider.
Now, we can test if the counter is initially equal to 0 or not.
Test if the counter increments and decrements correctly:
TextContext.test.js
(add the following code block to the file)
it('increments the counter', () => { const { getByTestId } = renderWithContext() fireEvent.click(getByTestId('button-up')) expect(getByTestId('counter')).toHaveTextContent('1') }) it('decrements the counter', () => { const { getByTestId} = renderWithContext() fireEvent.click(getByTestId('button-down')) expect(getByTestId('counter')).toHaveTextContent('-1') })
As you can see, here we fire a click event to test if the counter increments correctly to 1 and decrements to -1.
That being said, we can now move to the next section and introduce React Router.
7. Testing React Router
If you want to dive into React Router, this article might help you. Otherwise, let's check the TestRouter.js
file.
TestRouter.js
import React from 'react' import { Link, Route, Switch, useParams } from 'react-router-dom' const About = () =>About page
const Home = () =>Home page
const Contact = () => { const { name } = useParams() return {name}
} const TestRouter = () => { const name = 'John Doe' return ( Home About Contact ) } export default TestRouter
Here, we have some components to render when navigating the Home page.
Now, let's write the tests:
TestRouter.test.js
import React from 'react' import { Router } from 'react-router-dom' import { render, fireEvent } from '@testing-library/react' import { createMemoryHistory } from 'history' import TestRouter from './TestRouter' const renderWithRouter = (component) => { const history = createMemoryHistory() return { ...render ( {component} ) } } it('should render the home page', () => { const { container, getByTestId } = renderWithRouter() const navbar = getByTestId('navbar') const link = getByTestId('home-link') expect(container.innerHTML).toMatch('Home page') expect(navbar).toContainElement(link) })
To test React Router, we have to first have a navigation history to start with. Therefore we use createMemoryHistory()
to well as the name guessed to create a navigation history.
Next, we use our helper function renderWithRouter()
to render the component and pass history
to the Router
component. With that, we can now test if the page loaded at the start is the Home page or not. And if the navigation bar is loaded with the expected links.
Test if it navigates to other pages with the parameters when we click on links:
TestRouter.test.js
(add the following code block to the file)
it('should navigate to the about page', ()=> { const { container, getByTestId } = renderWithRouter() fireEvent.click(getByTestId('about-link')) expect(container.innerHTML).toMatch('About page') }) it('should navigate to the contact page with the params', ()=> { const { container, getByTestId } = renderWithRouter() fireEvent.click(getByTestId('contact-link')) expect(container.innerHTML).toMatch('John Doe') })
Now, to check if the navigation works, we have to fire a click event on the navigation links.
For the first test, we check if the content is equal to the text in the About Page, and for the second, we test the routing params and check if it passed correctly.
We can now move to the final section and learn how to test an Axios request.
We're almost done!

8. Testing HTTP Request
As usual, let's first see what the TextAxios.js
file looks like.
TextAxios.js
import React from 'react' import axios from 'axios' const TestAxios = ({ url }) => { const [data, setData] = React.useState() const fetchData = async () => { const response = await axios.get(url) setData(response.data.greeting) } return ( Load Data { data ? {data} : Loading...
} ) } export default TestAxios
As you can see here, we have a simple component that has a button to make a request. And if the data is not available, it will display a loading message.
Now, let's write the tests.
Test if the data are fetched and displayed correctly:
TextAxios.test.js
import React from 'react' import { render, waitForElement, fireEvent } from '@testing-library/react' import axiosMock from 'axios' import TestAxios from './TestAxios' jest.mock('axios') it('should display a loading text', () => { const { getByTestId } = render() expect(getByTestId('loading')).toHaveTextContent('Loading...') }) it('should load and display the data', async () => { const url = '/greeting' const { getByTestId } = render() axiosMock.get.mockResolvedValueOnce({ data: { greeting: 'hello there' }, }) fireEvent.click(getByTestId('fetch-data')) const greetingData = await waitForElement(() => getByTestId('show-data')) expect(axiosMock.get).toHaveBeenCalledTimes(1) expect(axiosMock.get).toHaveBeenCalledWith(url) expect(greetingData).toHaveTextContent('hello there') })
This test case is a bit different because we have to deal with an HTTP request. And to do that, we have to mock an axios request with the help of jest.mock('axios')
.
Now, we can use axiosMock
and apply a get()
method to it. Finally we will use the Jest function mockResolvedValueOnce()
to pass the mocked data as a parameter.
With that, now for the second test we can click to the button to fetch the data and use async/await to resolve it. And now we have to test 3 things:
- If the HTTP request has been done correctly
- If the HTTP request has been done with the
url
- If the data fetched matches the expectation.
And for the first test, we just check if the loading message is displayed when we have no data to show.
That being said, we're now done with the 8 simple steps to start testing your React Apps.
Don't be scared to test anymore.

Final Thoughts
The React Testing Library is a great package for testing React Apps. It gives us access to jest-dom
matchers we can use to test our components more efficiently and with good practices. Hopefully this article was useful, and it will help you build robust React apps in the future.
You can find the finished project here
Thanks for reading it!
Read more articles - Subscribe to my newsletter - Follow me on twitter
You can read other articles like this on my blog.
Next Steps
React Testing Library docs
React Testing Library Cheatsheet
Jest DOM matchers cheatsheet
Jest Docs