If you are a person who just finished Learning React Framework and got fairly good at it. You start giving Interviews, You almost got the skills for developing Single Page applications. You had a decent conversation with the interviewee until he asks you
This Blog covers the basic understanding of Testing in React.
As a developer, our primary goal is to build software that WORKS!
It doesn’t matter how much thought we put into improving user experience and performance if it doesn’t work. We check if our software works as it’s expected.
Jest is a javascript testing framework.
Jest is a test runner that finds tests, runs the tests, determines whether the tests passed or failed, and reports it back in a human-readable manner.
If you wanna know how Jest is written: The blog by kent C. Dodds
React out of the box comes with jest and RTL with useful defaults. If we look at `package.json` inside the project folder, we see testing-library package dependencies.
javascript"dependencies": {"@testing-library/jest-dom": "^5.16.5","@testing-library/react": "^13.4.0","@testing-library/user-event": "^14.4.3"...."react-scripts": "5.0.1",}
we don’t see jest package? However, we do see the react-scripts package. If we open the node_modules folder that pins down the react-scripts package.json. We see Jest as its dependency.
So in the node_module folder, Jest does exist even though it is not a direct dependency created with `create-react-app`.
It is a javascript utility that provides virtual DOM for testing React Components.
There's no virtual DOM while running a test. So the React Testing library provides a virtual DOM which we can use to interact with and verify the behavior of a react component.
The Core library is called DOM Testing Library and RTL is simply a wrapper around this core Library to test React applications in an easier way.
An Automated test is a code that throws an error when the actual output does not match the expected output.
Create a new component in your freshly installed create-app inside src/components/HelloWorld.
Let’s name it Helloworld.tsx
Helloworld.tsxexport const HelloWorld = () => {return <div>Hello World</div>}
now let’s write a test for it. In the same directory create a new file with the name Helloworld.test.tsx
Now let’s remember all we need is a test method that is globally available in create-react-app project
test() method two parameters,
Inside the second parameter. Call render method from testing-library with the Helloworld.tsx as a component.
Now when we have a virtual DOM let’s check if the text Hello World is present.
import screen at the top and use getByText available on the object and let’s catch it in a constant helloworldElement.
Finally, we can use expect method from jest to test our assertion.
Helloworld.test.tsximport {render,screen} from "@testing-library/react"test('Helloworld renders successfully', () => {render(<Helloworld />) // this is where a virtual DOM is createdconst helloWorldElement = screen.getByText('Hello World')expect(helloWorldElement).toBeInTheDocument()}
Now in the terminal run npm run test or yarn test
A test is useless if it doesn’t fail. So Let’s make a small change in the component to check if the test is running as expected.
Helloworld.tsxexport const HelloWorld = () => {return <div>Hello World of testing</div>}
Let’s get a little deep into RTL. Starting with the RTL Queries.
Queries are the methods that Testing Library provides to find elements on the page.
To find a single element on the page. We have
To find multiple elements on the page. We have
each of the methods can be suffixed to form an actual query.
The suffix can be one of Role, LabelText, PlaceholderText, Text, DisplayValue, AltText, Title, and finally TestId.
getByRole queries for elements with the given role.
Role refers to the ARIA (Accessible Rich Internet Applications) role which provides semantic meaning to the content to ensure people using assistive technologies are able to use them.
Let’s look at an example of the semantic roles of HTML elements
if you’re working with elements that do not have a default role or if you want to specify a different role, the role attribute can be used to add the desired role. example navbar links can have button role=’button’.
Let's look at another example. We have a fairly simple component that renders a form
Form.tsxconst Form = () => {return (<form><div><label htmlFor="name">Name</label><input type="text" id="name" /></div></form>)}export default Form
It’s corresponding test that asserts if the given textBox input renders
here we make use of getByRole query from RTL.
Form.test.tsximport { render, screen } from '@testing-library/react'import Form from './Form'test('Form renders correctly', () => {render(<Form />)const nameInputElement = screen.getByRole('textbox')expect(nameInputElement).toBeInTheDocument()})
Now let’s add more different types of input in the form.
Form.tsxconst Form = () => {return (<form><div><label htmlFor="name">Name</label><input type="text" id="name" /></div><div><input type="checkbox" /></div></form>)}export default Form
When two or more tests are there for a component we can combine the tests inside a describe method. describe also takes two parameters, the first is the string, and the second is the callback function.
This time we make use of the ‘checkbox’ property for getByRole method.
Watch for the terminal and it succeeds
Form.test.tsximport { render, screen } from '@testing-library/react'import Form from './Form'describe('Form', () => {test('Renders correctly', () => {render(<Form />)const nameInputElement = screen.getByRole('textbox')expect(nameInputElement).toBeInTheDocument()})test('Renders a checkbox correclty', () => {render(<Form />)const checkboxElement = screen.getByRole('checkbox')expect(checkboxElement).toBeInTheDocument()})})
Your first priority to access should always be getByRole , if for a reason getByRole can't be used. Look for getByLabelText, it really helps working with forms. Followed by the rest of the methods. If in case nothing can be used the last option is to use getByTestId.
Let’s start with a simple component that lists downs the name of cities and it’s corresponding tests
Cities.tsxtype CitiesProps = {cities: string[]}const Cities = (props: CitiesProps) => {const { cities } = propsreturn (<div><h1>Famous cities in the world</h1><ul>{cities.map((city: string) => {return <li key={city}>{city}</li>})}</ul></div>)}export default Cities
Cities.test.tsximport { render, screen } from '@testing-library/react'import Cities from './Cities'describe('Cities', () => {const cities = ['Paris', 'New York', 'London', 'Bangkok']test('renders correctly', () => {render(<Cities cities={cities} />)const headingElement = screen.getByRole('heading', {name: 'Famous cities in the world',level: 1,})expect(headingElement).toBeInTheDocument()})test('renders a list of cities', () => {render(<Cities cities={cities} />)const listBoxElement = screen.getByRole('list')expect(listBoxElement).toBeInTheDocument()})test('renders a list items of cities', () => {render(<Cities cities={cities} />)const listItemsElement = screen.getAllByRole('listitem')expect(listItemsElement).toHaveLength(cities.length)})})
This is a small blog that should get you started with testing components in react. In the Next blog let’s look at how to mock user events in react tests.
Thank you for reading, I hope this helps in your journey.
Keep rocking, Keep coding 💻