Key Bind to Snapshot Again Jest

How to Start Testing Your React Apps Using the React Testing Library and Jest

Testing is often seen as a tedious process. Information technology's actress code y'all accept to write, and in some cases, to be honest, information technology'due south not needed. Just every developer should know at least the basics of testing. It increases confidence in the products they build, and for most companies, information technology's a requirement.

In the React world, there is an amazing library called the react-testing-library which helps yous examination your React Apps more than efficiently. You use information technology with Jest.

In this article, we volition run across the eight simple steps you can have to start testing your React Apps like a dominate.

  • Prerequisites
  • Basics
  • What is React Testing Library?
  • 1. How to create a exam snapshot?
  • two. Testing DOM elements
  • 3. Testing events
  • 4. Testing asynchronous actions
  • 5. Testing React Redux
  • 6. Testing React Context
  • vii. Testing React Router
  • 8. Testing HTTP Asking
  • Final Thoughts
  • Next Steps

Prerequisites

This tutorial assumes that you have at least a basic understanding of React. I volition focus simply on the testing part.

And to follow along, you take to clone the project past running in your final:

                                  git clone https://github.com/ibrahima92/prep-react-testing-library-guide                              

Adjacent, run:

                                  yarn                              

Or, if you use NPM:

                npm install                              

And that's it! Now let'southward swoop into some basics.

Basics

Some key things will be used a lot in this commodity, and understanding their role tin help y'all with your understanding.

information technology or test: describes the test itself. It takes as parameters the name of the test and a function that holds the tests.

expect: the condition that the exam needs to pass. It will compare the received parameter to a matcher.

a matcher: a function that is applied to the expected status.

render: the method used to render a given component.

                import React from 'react' import {return} from '@testing-library/react' import App from './App'    information technology('should take a snapshot', () => {     const { asFragment } = render(<App />)          expect(asFragment(<App />)).toMatchSnapshot()    }) });                              

As yous tin run across, we describe the examination with it, then, utilize render to brandish the App component and expect that asFragment(<App />) matches toMatchSnapshot() (the matcher provided by jest-dom).

Past the manner, the render method returns several methods we tin use to test our features. We likewise used destructuring to get the method.

That being said, allow's move on and learn more virtually the React Testing Library in the adjacent department.

What is the React Testing Library?

The React Testing Library is a very lite-weight package created by Kent C. Dodds. Information technology'due south a replacement for Enzyme and provides light utility functions on top of react-dom and react-dom/test-utils.

The React Testing Library is a DOM testing library, which means that instead of dealing with instances of rendered React components, information technology handles DOM elements and how they behave in front of real users.

It'southward a slap-up library, information technology's (relatively) like shooting fish in a barrel to start using, and it encourages proficient testing practices. Notation – you can also use information technology without Jest.

"The more your tests resemble the mode your software is used, the more confidence they tin can give you lot."

So, let'south start using it in the next section. By the style, you lot don't need to install any packages, since create-react-app comes with the library and its dependencies.

1. How to create a test snapshot

A snapshot, as the name suggests, allows u.s.a. to save the snapshot of a given component. It helps a lot when you update or practise some refactoring, and want to get or compare the changes.

Now, let's accept a snapshot of the App.js file.

  • 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(<App />)          look(asFragment(<App />)).toMatchSnapshot()    }) });                              

To take a snapshot, we first have to import return and cleanup. These two methods will be used a lot throughout this article.

render, every bit you might guess, helps to return a React component. And cleanup is passed as a parameter to afterEach to just clean upward everything after each test to avoid memory leaks.

Adjacent, we can render the App component with render and get back asFragment every bit a returned value from the method. And finally, make sure that the fragment of the App component matches the snapshot.

Now, to run the test, open your final and navigate to the root of the project and run the following command:

                                  yarn test                              

Or, if you use npm:

                                  npm examination                              

As a result, information technology will create a new folder __snapshots__ and a file App.test.js.snap in the src which will expect like this:

  • App.test.js.snap
                // Jest Snapshot v1, https://goo.gl/fbAQLP  exports[`Take a snapshot should accept a snapshot 1`] = ` <DocumentFragment>   <div class="App">     <h1>Testing</h1>   </div> </DocumentFragment> `;                              

And if you make some other change in App.js, the test will fail, because the snapshot will no longer friction match the condition. To make it passes, just press u to update it. And you'll accept the updated snapshot in App.examination.js.snap.

Now, let's movement on and commencement testing our elements.

2. Testing DOM elements

To test our DOM elements, nosotros first take to look at the TestElements.js file.

  • TestElements.js
                import React from 'react'  const TestElements = () => {  const [counter, setCounter] = React.useState(0)     return (   <>     <h1 data-testid="counter">{ counter }</h1>     <button data-testid="button-up" onClick={() => setCounter(counter + 1)}> Up</button>     <button disabled data-testid="button-down" onClick={() => setCounter(counter - 1)}>Downwards</push button>  </>     )   }    consign default TestElements                              

Here, the merely affair 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.exam.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 } = return(<TestElements />);      expect(getByTestId('counter')).toHaveTextContent(0)    });                              

As you tin see, the syntax is quite similar to the previous examination. The only difference is that nosotros employ getByTestId to select the necessary elements (recall the data-testid) and check if information technology passed the examination. In others words, we check if the text content <h1 data-testid="counter">{ counter }</h1> is equal to 0.

Test if the buttons are enabled or disabled:

TestElements.test.js (add the following code block to the file)

                                  it('should be enabled', () => {     const { getByTestId } = render(<TestElements />);     expect(getByTestId('button-upwards')).not.toHaveAttribute('disabled')   });    it('should be disabled', () => {     const { getByTestId } = return(<TestElements />);      expect(getByTestId('push-down')).toBeDisabled()   });                              

Here, as usual, we use getByTestId to select elements and check for the first exam if the button has a disabled attribute. And for the 2nd, if the push button is disabled or not.

And if you save the file or run again in your final yarn test, the test volition pass.

Congrats! Your showtime test has passed!

congrats

Now, let's learn how to test an event in the next section.

3. Testing events

Before writing our unit tests, permit's first bank check what the TestEvents.js looks like.

  • TestEvents.js
                import React from 'react'  const TestEvents = () => {   const [counter, setCounter] = React.useState(0)    return (   <>     <h1 data-testid="counter">{ counter }</h1>     <button information-testid="button-up" onClick={() => setCounter(counter + 1)}> Up</button>     <button data-testid="push-downwards" onClick={() => setCounter(counter - 1)}>Down</button>  </>     )   }      export default TestEvents                              

At present, allow'due south 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(<TestEvents />);           fireEvent.click(getByTestId('button-up'))      expect(getByTestId('counter')).toHaveTextContent('1')   });    it('decrements counter', () => {     const { getByTestId } = render(<TestEvents />);           fireEvent.click(getByTestId('push button-downward'))      expect(getByTestId('counter')).toHaveTextContent('-1')   });                              

As you can see, these two tests are very similar except the expected text content.

The get-go examination fires a click consequence with fireEvent.click() to cheque if the counter increments to i when the push is clicked.

And the second one checks if the counter decrements to -one when the push is clicked.

fireEvent has several methods you tin utilize to test events, so feel complimentary to dive into the documentation to acquire more than.

Now that we know how to test events, permit's move on and learn in the adjacent section how to deal with asynchronous actions.

iv. Testing asynchronous actions

An asynchronous action is something that can take time to complete. It can be an HTTP asking, a timer, and so on.

Now, allow's bank 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 (   <>     <h1 information-testid="counter">{ counter }</h1>     <push button data-testid="push button-up" onClick={delayCount}> Upwards</button>     <button data-testid="button-downwards" onClick={() => setCounter(counter - 1)}>Down</button>  </>     )   }      export default TestAsync                              

Here, we use setTimeout() to delay the incrementing upshot 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 later on 0.5s', async () => {     const { getByTestId, getByText } = render(<TestAsync />);       fireEvent.click(getByTestId('push-up'))      const counter = await waitForElement(() => getByText('1'))       expect(counter).toHaveTextContent('1')   });                              

To test the incrementing consequence, we start have to use async/await to handle the activity because, as I said before, 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.

At present, afterwards clicking to the button, we look for the counter to be incremented with waitForElement(() => getByText('one')). And once the counter incremented to one, nosotros can now move to the condition and bank check if the counter is effectively equal to 1.

That being said, let'south now move to more complex exam cases.

Are you lot ready?

ready

5. Testing React Redux

If yous'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, acceleration}) => {   const increment = () => dispatch({ type: 'INCREMENT' })  const decrement = () => dispatch({ type: 'DECREMENT' })     return (   <>     <h1 data-testid="counter">{ counter }</h1>     <button information-testid="push button-up" onClick={increment}>Up</push button>     <button data-testid="push button-downwardly" onClick={decrement}>Down</button>  </>     )   }    export default connect(state => ({ counter: state.count }))(TestRedux)                              

And for the reducer:

  • store/reducer.js
                consign const initialState = {     count: 0,   }      consign office reducer(state = initialState, activity) {     switch (activity.type) {       example 'INCREMENT':         return {           count: state.count + 1,         }       case 'DECREMENT':         return {           count: state.count - 1,         }       default:         render state     }   }                              

As y'all tin see, there is nothing fancy – it'due south just a basic Counter Component handled by React Redux.

Now, let'south write the unit tests.

Test if the initial country 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(<Provider store={store}>{component}</Provider>),     store,   } }   afterEach(cleanup);  it('checks initial state is equal to 0', () => {     const { getByTestId } = renderWithRedux(<TestRedux />)     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 exist used several times.

renderWithRedux() receives every bit parameters the component to render, the initial country, and the shop. If there is no store, information technology 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, nosotros can now laissez passer 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(<TestRedux />,      {initialState: {count: v} })   fireEvent.click(getByTestId('push button-up'))   wait(getByTestId('counter')).toHaveTextContent('6') })  it('decrements the counter through redux', () => {   const { getByTestId} = renderWithRedux(<TestRedux />, {     initialState: { count: 100 },   })   fireEvent.click(getByTestId('button-downward'))   await(getByTestId('counter')).toHaveTextContent('99') })                              

To test the incrementing and decrementing events, we pass an initial country as a 2d statement to renderWithRedux(). Now, nosotros can click on the buttons and test if the expected result matches the condition or not.

Now, let'southward move to the next department and innovate React Context.

React Router and Axios will come up side by side – are you still with me?

of-course

half dozen. Testing React Context

If you're new to React Context, bank check out this article first. Otherwise, allow'south bank 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)    render (     <CounterContext.Provider value={{ counter, increment, decrement }}>       <Counter />     </CounterContext.Provider>   ) }  export const Counter = () => {       const { counter, increase, decrement } = React.useContext(CounterContext)        return (      <>        <h1 information-testid="counter">{ counter }</h1>        <button data-testid="button-up" onClick={increment}> Up</push>        <button data-testid="push button-down" onClick={decrement}>Downward</push button>     </>        ) }  export default CounterProvider                              

Now, the counter state is managed through React Context. Let's write the unit examination to bank check if it behaves as expected.

Exam 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(         <CounterProvider value={CounterContext}>             {component}         </CounterProvider>)   } }  afterEach(cleanup);  information technology('checks if initial state is equal to 0', () => {     const { getByTestId } = renderWithContext(<Counter />)     expect(getByTestId('counter')).toHaveTextContent('0') })                              

As in the previous section with React Redux, hither we use the same approach, by creating a helper role renderWithContext() to render the component. But this time, information technology receives simply the component every bit a parameter. And to create a new context, nosotros pass CounterContext to the Provider.

Now, we can test if the counter is initially equal to 0 or non.

Examination if the counter increments and decrements correctly:

TextContext.exam.js (add together the following code block to the file)

                                  it('increments the counter', () => {     const { getByTestId } = renderWithContext(<Counter />)      fireEvent.click(getByTestId('button-up'))     expect(getByTestId('counter')).toHaveTextContent('i')   })    it('decrements the counter', () => {     const { getByTestId} = renderWithContext(<Counter />)      fireEvent.click(getByTestId('push-downwards'))     await(getByTestId('counter')).toHaveTextContent('-1')   })                              

Equally you can run across, here we burn down a click issue to test if the counter increments correctly to one and decrements to -1.

That beingness said, we tin can now motility to the next section and introduce React Router.

seven. Testing React Router

If you desire to swoop into React Router, this article might assist you. Otherwise, permit's check the TestRouter.js file.

  • TestRouter.js
                import React from 'react' import { Link, Route, Switch,  useParams } from 'react-router-dom'  const About = () => <h1>Well-nigh page</h1>  const Home = () => <h1>Home page</h1>  const Contact = () => {   const { proper noun } = useParams()   return <h1 information-testid="contact-proper name">{name}</h1> }  const TestRouter = () => {     const proper name = 'John Doe'     return (     <>     <nav data-testid="navbar">       <Link data-testid="dwelling house-link" to="/">Dwelling</Link>       <Link information-testid="about-link" to="/about">About</Link>       <Link data-testid="contact-link" to={`/contact/${proper name}`}>Contact</Link>     </nav>            <Switch>         <Route exact path="/" component={Home} />         <Route path="/about" component={About} />         <Route path="/about:name" component={Contact} />       </Switch>     </>   ) }  consign default TestRouter                              

Here, we have some components to render when navigating the Home page.

Now, allow'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 (     <Router history={history}>         {component}     </Router>     )   } }  it('should return the home page', () => {    const { container, getByTestId } = renderWithRouter(<TestRouter />)    const navbar = getByTestId('navbar')   const link = getByTestId('dwelling-link')    expect(container.innerHTML).toMatch('Abode folio')   expect(navbar).toContainElement(link) })                              

To test React Router, we have to offset have a navigation history to start with. Therefore we use createMemoryHistory() to well every bit the proper noun guessed to create a navigation history.

Next, nosotros use our helper role renderWithRouter() to return the component and laissez passer history to the Router component. With that, nosotros can now exam if the folio loaded at the outset 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 virtually page', ()=> {   const { container, getByTestId } = renderWithRouter(<TestRouter />)     fireEvent.click(getByTestId('virtually-link'))    expect(container.innerHTML).toMatch('Near page') })  it('should navigate to the contact folio with the params', ()=> {   const { container, getByTestId } = renderWithRouter(<TestRouter />)        fireEvent.click(getByTestId('contact-link'))       expect(container.innerHTML).toMatch('John Doe') })                              

Now, to check if the navigation works, we have to burn a click issue on the navigation links.

For the get-go test, nosotros check if the content is equal to the text in the About Page, and for the 2nd, we test the routing params and check if it passed correctly.

We can now move to the final section and learn how to exam an Axios request.

We're nearly washed!

still-here

viii. Testing HTTP Asking

As usual, let'south 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 = look axios.get(url)     setData(response.data.greeting)      }         return (   <>     <push onClick={fetchData} data-testid="fetch-data">Load Data</button>     {      information ?     <div data-testid="testify-data">{data}</div>:     <h1 data-testid="loading">Loading...</h1>     }   </>      ) }  export default TestAxios                              

As you can see here, we have a simple component that has a button to make a request. And if the information is non bachelor, it will display a loading message.

Now, let's write the tests.

Exam if the data are fetched and displayed correctly:

TextAxios.exam.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 brandish a loading text', () => {   const { getByTestId } = render(<TestAxios />)    expect(getByTestId('loading')).toHaveTextContent('Loading...') })  it('should load and brandish the data', async () => {   const url = '/greeting'   const { getByTestId } = render(<TestAxios url={url} />)    axiosMock.get.mockResolvedValueOnce({     data: { greeting: 'hello there' },   })    fireEvent.click(getByTestId('fetch-data'))    const greetingData = await waitForElement(() => getByTestId('show-data'))    expect(axiosMock.go).toHaveBeenCalledTimes(i)   expect(axiosMock.go).toHaveBeenCalledWith(url)   expect(greetingData).toHaveTextContent('hello there') })                              

This test example is a flake different considering we take to deal with an HTTP request. And to exercise that, we accept to mock an axios request with the assist of jest.mock('axios').

Now, we tin can utilize axiosMock and apply a get() method to it. Finally we will employ the Jest office 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:

  1. If the HTTP request has been washed correctly
  2. If the HTTP request has been washed with the url
  3. If the information fetched matches the expectation.

And for the commencement test, we but check if the loading message is displayed when we accept no data to show.

That existence said, we're at present done with the 8 elementary steps to start testing your React Apps.

Don't exist scared to test anymore.

not-scared

Final Thoughts

The React Testing Library is a corking package for testing React Apps. It gives us access to jest-dom matchers we can use to test our components more efficiently and with skilful practices. Hopefully this article was useful, and it will help y'all build robust React apps in the future.

You can detect the finished project hither

Thanks for reading information technology!

Read more than manufactures  -  Subscribe to my newsletter   -   Follow me on twitter

Y'all 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



Larn to code for free. freeCodeCamp'southward open source curriculum has helped more 40,000 people get jobs as developers. Get started

garciafortaged.blogspot.com

Source: https://www.freecodecamp.org/news/8-simple-steps-to-start-testing-react-apps-using-react-testing-library-and-jest/

0 Response to "Key Bind to Snapshot Again Jest"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel