React Testing
React Testing using Testing library and Jest
What is Jest?
Jest is a delightful JavaScript Testing Framework with a focus on simplicity.
It works with projects using: Babel, TypeScript, Node, React, Angular, Vue and more!
What is Jest-DOM?
Jest Dom comes with create-react-app
It uses src/setup.test.js and the Jest DOM is available on all these files ending with .test.js
The above point means that the Jest DOM matchers are available in .test.js files where Jest DOM provides some of the methods that will test the screen elements -> "Tests if element is present on DOM or not" and many more methods related to screen/UI which is DOM.
The normal Jest doesn't do the testing of screen elements. It might do test of Javascript related stuff, for example, jest can test how many elements are present in an array. On the other hand, Jest DOM can test if UI shows "Learn React" or not.
Simply put, because of Jest DOM, the DOM matchers for Jest is available on .test.js files
What is React Testing Library?
Provides virtual DOMs for tests - Any time we are running tests without a browser, we need to have a virtual DOM so we can do things like click elements and we can see if virtual DOM behaves like it should do.
How Jest is related to React Testing Library?
Jest is a test runner. Jest is responsible for
Finding Tests
Running the tests
Determining whether the tests pass or fail
Both React Testing Library and Jest works together.
Relationship between React-Testing-Library and Jest


What is Enzyme and Mocha?
Enzyme is replacement for React Testing Library like it's said in the stackoverflow link above. It's not the replacement for Jest.
Mocha is replacement for Jest.
Below combination are possible
React Testing Library + Jest -> Our Udemy Course and popular option
Enzyme + Jest
React Testing Library + Mocha
Enzyme + Mocha
How to run tests?
To run the create-react-app we use
npm start
To run the tests, we use
npm test
Type 'a' to run our tests. This is our watch mode (More on this later)

Assertions
Assertions are the statements that test our expectations against actual results.


How Jest Works?


What is Test Driven Development (TDD)?
Writing the tests even before writing the code.


What are the types of Tests?
Unit Testing
Tests one unit of code at a time. May be a single function or a component without depending on other units
Integration Testing
Tests how multiple units work together. Testing interaction between different functions/units/components
Functional / Behavioural Testing
Functional testing means testing the behaviour of a function/software. We might be testing if the software does the right thing with the particular set of data. That might be an integration test as it might have to interact with different units. So the functional test can be an integration test as well
The functional test can also be a simple unit test. Let's say on a button click, the div turns red. This might be a simple unit test but still it can be considered as functional test as well as it tests for a particular behaviour of CLICK TURNS RED OR NOT
So, the functional test means, not testing the code but testing the behaviour
React-Testing-Library encourages functional tests
Acceptance/End-to-End(E2E) test
This is an End-to-End testing where we need a browser and might also need the server
Popular tools for E2E testing are Cypress and Selenium
React-Testing-Library doesn't support E2E testing

TDD (Test-Driven-Development) Vs BDD (Behavioural Driven Development)
If only developer is testing the code while writing the code then its TDD even though we write functional tests for testing behaviour
BDD is when different teams are involved like developer, QA, business and so on to test the software

Accessibility and Finding Elements with Testing Library
Testing library recommends finding elements by accessibility handles like screen readers would be able to find them
Priorities to access the elements (For more details refer the above docs)
Queries Accessible to everyone. Examples:
getByRole - All the role definitions can be found here https://www.w3.org/TR/wai-aria-1.1/#role_definitions
Most of the HTML / JSX elements has the role by default. For Example, the link is the default role of anchor tag <a> and we don't need to explicitly define it.
To define the role explicitly we say
role = ""
. Example,<a role="link"/>

getByLabelText
2. Semantic Queries
getByAltText
getByTitle
3. Test IDs
getByTestId
Read more about this in the document given below.
If our test can't find an element like the screen reader would then our app is not friendly to the screen readers which is a bad thing ๐ข
Course Plan to learn React-Testing

First App - Color Button

In the first app, we just test the change of the colour of the button on click. If we click on red button, it will change to blue and vice versa.
Task 1 - Test the background color of button if it's red
Step 1
In TDD style, we first write the test before we test the functionality and make it fail
First clear the content in
App.js
and make sure it just returns a simplediv
Write the test for this first and see it fail and then in step 2, write enough to make the test pass

import { render, screen } from '@testing-library/react';
import App from './App';
test('button has correct initial color', () => {
// first, render the app
render(<App />)
// second, find the element. That will be done by global object - screen that has access to virtual dom
const colorButton = screen.getByRole('button', { name: 'Change To blue' }) // find element with a role of button and text 'Change to blue'
// third, expect the bgcolor to red
expect(colorButton).toHaveStyle({ backgroundColor: 'red' })
})
Step 2
Once the test fails, then write enough to make the test pass in
App.js
file

Task 2 - Test the button when clicked changes the colour to blue
Steps
First write the test and let it fail
Now write the function to make the button colour to blue and then see the test pass
Test Before Code
test('button turns blue on click', () => {
render(<App />)
const colorButton = screen.getByRole('button', { name: 'Change To Blue' })
fireEvent.click(colorButton)
// expect the change of color to blue on click
expect(colorButton).toHaveStyle({ backgroundColor: 'Blue' })
//expect the change of text to Change To Red as well after click
expect(colorButton.textContent).toBe('Change To Red')
// OR
// expect(colorButton).toHaveTextContent('Change To Red')
})
// Note : We can write two assert statements sometimes as this is a
// functional/behavioural testing
Code after Test fails
function App() {
const [buttonColor, setButtonColor] = React.useState('Red')
const newButtonColor = buttonColor === 'Red' ? 'Blue' : 'Red'
return (
<div>
<button onClick={() => setButtonColor(newButtonColor)} style={{ backgroundColor: buttonColor }}>Change To {newButtonColor}</button>
</div>
);
}
export default App;
Task 3 - Let's add Checkbox to the page. When checkbox is checked, the button should be disabled and when unchecked, the button gets enabled
Steps
Write the test for initial condition - to check if the colorButton is enabled and the checkbox is disabled
The below test fails as it can't find checkbox
test('initial conditions', () => {
// the button starts out enabled
render(<App />)
const colorButton = screen.getByRole('button', { name: 'Change To Blue' })
expect(colorButton).toBeEnabled() // checks the button if enabled
// check that the checkbox is unchecked
const checkbox = screen.getByRole('checkbox')
expect(checkbox).not.toBeChecked()
})
Let's make the test pass by adding a checkbox into App.js
function App() {
const [buttonColor, setButtonColor] = React.useState('Red')
const newButtonColor = buttonColor === 'Red' ? 'Blue' : 'Red'
return (
<div>
<button onClick={() => setButtonColor(newButtonColor)} style={{ backgroundColor: buttonColor }}>Change To {newButtonColor}</button>
<input type="checkbox" /> // added to make the above test pass
</div>
);
}
Let's test for button being disabled after checkbox being checked
test('test for button being disabled after checkbox being checked', () => {
render(<App />)
// get the checkbox
const checkbox = screen.getByRole('checkbox')
// first check the checkbox through fire-even
fireEvent.click(checkbox)
// get the button
const colorButton = screen.getByRole('button', { name: 'Change To Blue' })
// then check if button is disabled
expect(colorButton).toBeDisabled()
})
test('when checkbox is unchecked, the button should be enabled', () => {
render(<App />)
const checkbox = screen.getByRole('checkbox')
const colorButton = screen.getByRole('button', { name: 'Change To Blue' })
fireEvent.click(checkbox)
fireEvent.click(checkbox)
expect(colorButton).toBeEnabled()
})
To make the test passed
import logo from './logo.svg';
import React from 'react'
import './App.css';
function App() {
const [buttonColor, setButtonColor] = React.useState('Red')
const [buttonEnabled, setButtonEnabled] = React.useState(false)
const newButtonColor = buttonColor === 'Red' ? 'Blue' : 'Red'
return (
<div>
<button disabled={buttonEnabled} onClick={() => setButtonColor(newButtonColor)} style={{ backgroundColor: buttonColor }}>Change To {newButtonColor}</button>
<input aria-checked={buttonEnabled}
type="checkbox" onClick={() => setButtonEnabled(prevState => !prevState)} />
</div>
);
}
export default App;
How to give a name to the checkbox?
Name of the checkbox comes from the label assigned to it

Task 4 - Now let's add functionality where the button colour turns grey
Steps
Disable checkbox
Check if button is grey
Enable checkbox
Check if button is red
Click the button to change colour to blue
Disable checkbox
Check if button is grey
Enable checkbox
Check if button is blue
test('turn the button to gray on disabling it', () => {
render(<App />)
const colorButton = screen.getByRole('button', { name: 'Change To Blue' })
const checkbox = screen.getByRole('checkbox', { name: 'Disable Button' })
// disable checkbox
fireEvent.click(checkbox)
// check if btn is gray
expect(colorButton).toBeDisabled()
expect(colorButton).toHaveStyle({ backgroundColor: 'grey' })
// enable checkbox
fireEvent.click(checkbox)
// check if btn is red
expect(colorButton).toHaveStyle({ backgroundColor: 'Red' })
// click btn to change color to blue
fireEvent.click(colorButton)
// disable checkbox
fireEvent.click(checkbox)
// check if button is gray
expect(colorButton).toHaveStyle({ backgroundColor: 'grey' })
// enable checkbox
fireEvent.click(checkbox)
// check if btn is blue
expect(colorButton).toHaveStyle({ backgroundColor: 'Blue' })
})
Now after the test fails, implement the passing code and re-test to check if functionality is passed
<button disabled={buttonEnabled}
onClick={() => setButtonColor(newButtonColor)}
style={{ backgroundColor: buttonEnabled ? 'grey' : buttonColor }}>
Change To {newButtonColor}
</button>
Unit Tests
Till now we were doing a functional/functionality/behavioural testing on components. Then what is a unit test? Let's say we have a function that is used by one or more components. We might want to test that function with different inputs (different edge cases) and this is called unit test.
Let's take an example here. Let's say our users are bored of red and blue colours and they want Mid night blue and Medium Violet Red.
Here, our function might take different type of inputs like camel case, single word and so on. But at the end, we need a camel case output.
Edge cases
Test for - Single word colour like 'red', 'blue' (with no inner capital letters)
Test for - Works for one inner capital letter like 'midnightBlue'
Test for - Works for multiple inner capital letters like 'mediumVioletRed'
Here's you can see that we need to test a single function with different edge case inputs. In this case we can group these test cases (as it is related to same test with different inputs). For grouping the test cases, we can use describe.
Grouping tests
describe statement is used to group the tests
describe('Spaces before camel-case capital letters', () => {
test('Works for no inner capital letters', () => {
expect(replaceCamelWithSpace('red')).toBe('red')
})
test('Works for word having one capital letter', () => {
expect(replaceCamelWithSpace('MidnightBlue')).toBe('Midnight Blue')
})
test('Works for word having multiple capital letters', () => {
expect(replaceCamelWithSpace('MediumVioletRed')).toBe('Medium Violet Red')
})
})
Function to make this pass
export function replaceCamelWithSpace(colorName) {
// replacing capitals with spaces if the capital letter appears in the middle
return colorName.replace(/\B([A-Z])\B/g, ' $1')
}
What all you learned?
FIRST APP - COLOUR BUTTON
How to test text content on screen
How to check if button is enabled and disabled
How to check if checkbox is enabled and disabled
SECOND APP
Last updated
Was this helpful?