How to Test a simple React component

How to Test a simple React component

In the first part of this series, we will learn how to test components with local state by testing the changes reflected in our UI.

Writing tests is important. And what better way to write them in the same way a user interacts with your app.

@testing-library/react created by Kent C. Dodds is a part of the testing library project that includes test utils for React and other UI libraries.

As Kent puts it, don't test your component state. Rather, test the UI reflected because of the changes in state as that will be the way any user interacts with your app.

This is why I am creating a series, where I will add examples with specific use cases.

The respository with the example below is here.

For the first example, we shall take a simple Checklist component that accepts a list of tasks to be displayed. We can toggle to check whether the task is completed or not and also view the count of the remaining tasks.

Note: I am using TypeScript for this project as it provides a great development experience, but you can create these examples in JavaScript as well by stripping of the types.

This is the task list component made with React Hooks. If you're not familiar with hooks, you can implement the same with class components.

import React, { useState, useMemo } from 'react'

interface Checklist { id: number text: string checked: boolean }

interface ChecklistProps { checklistItems: Checklist[] }

const Checklist: React.FC<ChecklistProps> = ({ checklistItems = [] }) => { let [checklist, updatelist] = useState<Checklist[]>(checklistItems)

const toggleComplete = (index: number) => { let newChecklist = [...checklist] newChecklist[index].checked = !newChecklist[index].checked updatelist(newChecklist) }

let checkedItems = useMemo(() => checklist.filter(c => c.checked).length, [ checklist, ])

return ( <div> <ul className="checklist" data-testid="items-list"> {checklist.map((checkitem, index) => ( <li key={checkitem.id} className="list-item"> <input type="checkbox" id={checkitem.id.toString()} checked={checkitem.checked} onChange={() => toggleComplete(index)} /> <label htmlFor={checkitem.id.toString()}>{checkitem.text}</label> </li> ))} </ul> <p data-testid="checked-items"> Checked {checkedItems} of {checklist.length} items </p> </div> ) }

Here we are displaying our tasks in an unordered list, and below that, the completed tasks count out of the total. You must have noticed that I have added a data-testid attribute to the ul tag which we will be using in our tests below to fetch the respective element.

So the first test we would write would be to test whether our list is rendering properly. And for that we will fetch the list element and assert whether it contains the same amount of tasks that we have passed.

import React from 'react'
import { render } from '@testing-library/react'
import Checklist from './Checklist'

const checklistItems = [ { id: 1, text: 'Learn React Testing Library', checked: false, }, { id: 2, text: 'Learn Advanced JS concepts', checked: false, }, ]

test(has rendered a the items passed correctly, () => { const { getByTestId } = render(<Checklist checklistItems={checklistItems} />) let ul = getByTestId('items-list') expect(ul.children.length).toEqual(2) })

Here we have a simple test block that has the required imports, our sample task list to be passed via props, and a render method from the testing library. This method will render our entire component and it's children, unlike the shallow method from enzyme which by it's name, does a shallow rendering of the component i.e. it skips rendering the child components.

Now render returns us a handful of methods that help us fetch the required elements present in the component. So we're fetching the ul element with the getByTestId method and the matcher-string i.e. items-list is the one we added on the ul in our component.

Note: This works just like the getElementById method in JavaScript but it matches a data-testid attribute instead of an id.

At last, we use the matchers that Jest provides and we can check for the length of the list-items to be equal to the tasks list that we are providing. Now, if you run this via npm test or yarn test, your test will pass!

Note: yarn test or npm test runs in watch mode by default so as you save your tests or components, it will automatically run those and you can view the output in your terminal.

Moving on, our second test would be to assert whether the checkboxes are functional and for that we need to interact with the task item, so we need to simulate a click event. And this library has just the method for that: fireEvent.

test(updates UI of checked item on toggling, () => {
  const { getByLabelText } = render(
    <Checklist checklistItems={checklistItems} />
  )

let firstItem = getByLabelText(checklistItems[0].text) as HTMLInputElement fireEvent.click(firstItem) expect(firstItem.checked).toBeTruthy()

fireEvent.click(firstItem) expect(firstItem.checked).toBeFalsy() })

Here we are rendering our Checklist component again in our test. And this is important, as it isolates our tests so that the previous test doesn't affect the next.

We fetch the first item in our task list using another utility method getByLabelText as we have used a label that will toggle our task completion, this method will find the input associated with the label.

After fetching the task, we simulate a click event on that item. Then we assert whether the checked property is truthy using the matchers that Jest provides. We then simulate the click event again to check whether the event is working properly and we successfully toggle the checkbox. On saving, if you check your terminal, the second test also passes!

Note: The latest version of @testing-library/react handles cleanup automatically after each test block so you don't need to add any cleanup logic to your tests!.

For our last test, we will verify the checked items count that we have rendered below our task list.

test(correctly shows the checked item length, () => {
  const { getByTestId, getByLabelText } = render(
    <Checklist checklistItems={checklistItems} />
  )
  let p = getByTestId('checked-items')

let firstItem = getByLabelText(checklistItems[0].text) as HTMLInputElement fireEvent.click(firstItem) expect(p.textContent).toContain('1 of 2')

let secondItem = getByLabelText(checklistItems[1].text) as HTMLInputElement fireEvent.click(secondItem) expect(p.textContent).toContain('2 of 2') })

Again we render our component in the test block, fetch the list item via the getByLabelText method, and we match the text of the p tag to contain the text that we pass using the toContain matcher.

Now, on saving the file, we can see in the terminal that all our tests pass! You will see that on running yarn start or npm start, our app works perfectly. We tested our component just as how we would interact with it. This was all about testing a component with local state.

Thanks for reading, further reading

☞ React - The Complete Guide (incl Hooks, React Router, Redux)

☞ Modern React with Redux [2019 Update]

☞ The Complete React Developer Course (w/ Hooks and Redux)

☞ React JS Web Development - The Essentials Bootcamp

☞ React JS, Angular & Vue JS - Quickstart & Comparison

☞ The Complete React Js & Redux Course - Build Modern Web Apps

☞ React JS and Redux Bootcamp - Master React Web Development


reactjs react-native testing web-development

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

How native is React Native? | React Native vs Native App Development

Article covers: How native is react native?, React Native vs (Ionic, Cordova), Similarities and difference between React Native and Native App Development.

Hire Dedicated React Native Developer

Have you ever thought of having your own app that runs smoothly over multiple platforms? React Native is an open-source cross-platform mobile application framework which is a great option to create mobile apps for both Android and iOS. **[Hire...

Hire Dedicated React Native Developer in India | React Native Development

Hire dedicated React Native developers for your next project. As the top react native development company we offer you the best service as per your business needs.

Which is the best React Native app development company in New York?

Hire top react native app development company in New York to build and develop custom react native mobile apps for Android & iOS with the latest features.

Top React Native Mobile App Development Companies in USA

Looking for top React Native mobile app development company in USA for Startups & Enterprise? Find out the top list of React Native mobile app development company in USA.