Introduction
Unit testing React components with TypeScript is a common practice to ensure the correctness of your components, especially when TypeScript is used in a React project. The process is quite similar to unit testing in JavaScript, but with some adjustments to accommodate TypeScript’s type system.
There are two popular testing libraries for React: Enzyme and React Testing Library.
In this guide we will be testing React components using React Testing Library and Jest. As RTL provides a simple and straightforward way to test components that promotes good test practices and Jest will take care of running tests and handling assertions.
We’ll walk through setting up your project, creating a simple registration form, and writing test cases for it.
Project Setup
Create a TypeScript and React module with Liferay:
Follow our blog post on TypeScript and React with Liferay to set up your module.
When you create a react app, you have some testing dependencies by default installed in your package.json. Make sure you have the following dependencies installed in your project:
@types/jest @testing-library/react @testing-library/jest-dom npm i @testing-library/user-event
Install additional dependencies:
[ npm install jest ts-jest jest-config jest-environment-jsdom ]
Configure Jest:
Run the following command to create a jest.config.json file:
[ npx jest –init ]
This will generate a jest.config.json file with default configurations. It includes mappings for module names and mocks. Additionally, it specifies the file extensions to be considered and sets the test environment to jsdom. Make sure your project structure and paths align with the configured mappings for the best results.
![Initializing Jest Configuration | Test React Typescript Component with Jest and RTL Initializing Jest Configuration](https://www.ignek.com/wp-content/uploads/2024/02/Initializing-Jest-Configuration-1024x549.webp)
Update tsconfig.json:
Add the Jest type to the compilerOptions:
[ “types”: “node”, “jest” ]
![Adding Jest Type to CompilerOptions | Test React Typescript Component with Jest and RTL Adding Jest Type to CompilerOptions](https://www.ignek.com/wp-content/uploads/2024/02/Adding-Jest-Type-to-CompilerOptions.webp)
Create tsconfig.test.json:
Create a tsconfig.test.json file with the following configuration:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"jsx": "react-jsx",
"esModuleInterop": true
}
}
Creating a Registration Form Component
Now that your project is set up, let’s create a simple registration form and write test cases for it and export default RegisterForm.
import React from "react";
import {
Grid,
makeStyles,
Card,
CardContent,
CardActions,
Button,
CardHeader,
} from "@material-ui/core";
import { Formik, Form, Field } from "formik";
import * as Yup from "yup";
import { TextField } from "formik-material-ui";
interface Values {
firstName: string;
lastName: string;
city: string;
email: string;
password: string;
}
const useStyles = makeStyles((theme: any) => ({
padding: { padding: theme.spacing(3) },
button: { margin: theme.spacing(1) },
}));
const lowercaseRegEx = /(?=.*[a-z])/;
const uppercaseRegEx = /(?=.*[A-Z])/;
const numericRegEx = /(?=.*[0-9])/;
const lengthRegEx = /(?=.{6,})/;
const validationSchema = Yup.object().shape({
firstName: Yup.string().required("Required"),
lastName: Yup.string().required("Required"),
email: Yup.string().email("Invalid email").required("Required"),
password: Yup.string()
.matches(
lowercaseRegEx,
"Must contain one lowercase alphabetical character!"
)
.matches(
uppercaseRegEx,
"Must contain one uppercase alphabetical character!"
)
.matches(numericRegEx, "Must contain one numeric character!")
.matches(lengthRegEx, "Must contain 6 characters!")
.required("Required!"),
});
const RegisterForm: React.FC = () => {
const classes = useStyles();
const onSubmit = (values: Values) => {
console.log(values);
};
return (
{({ dirty, isValid, values, handleChange, handleBlur }) => {
return (
);
}}
);
};
Writing Test Cases
Folder Structure
Create a __tests__ folder inside your component folder for test files.
![Creating Test Folder for Component | Test React Typescript Component with Jest and RTL Creating Test Folder for Component](https://www.ignek.com/wp-content/uploads/2024/02/Creating-Test-Folder-for-Component.webp)
The use of a special folder name like “__tests__” signifies that the content of this folder is related to testing. This can be beneficial when someone new joins a project or when revisiting the code after some time. However, it’s important to note that the use of “__tests__” is a convention and not a strict rule. In Jest, the “__tests__” convention is recognized by default, but Jest is flexible enough to work with other configurations or file structures if needed. Always refer to the documentation of the testing framework you are using to understand its conventions and customization options.
Writing Unit Tests
Create Formtable.test.tsx file inside the __ tests__ folder for writing test cases for react code.Here, we will create a unit test for that Form.tsx component. First of all, make sure we have created Form.test.tsx on /components/__test__/Form.test.tsx.
import { render, screen, fireEvent, act } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import RegisterForm from "../components/Form";
describe("RegisterForm component", () => {
test("can fill out the form and submit", async () => {
render( );
await act(async () => {
const firstNameInput = screen.getByLabelText("First Name");
const lastNameInput = screen.getByLabelText("Last Name");
const cityInput = screen.getByLabelText("City");
const emailInput = screen.getByLabelText("Email");
const passwordInput = screen.getByLabelText("Password");
const registerButton = screen.getByText("REGISTER");
userEvent.type(firstNameInput, "John");
userEvent.type(lastNameInput, "Doe");
userEvent.type(cityInput, "Sample City");
userEvent.type(emailInput, "john.doe@example.com");
userEvent.type(passwordInput, "Password123");
fireEvent.click(registerButton);
});
});
test("displays validation error for invalid email", async () => {
render( );
await act(async () => {
const emailInput = screen.getByLabelText("Email");
const registerButton = screen.getByText("REGISTER");
userEvent.type(emailInput, "invalidemail");
fireEvent.click(registerButton);
});
// Add assertion to check for the expected error message
expect(screen.getByText("Invalid email")).toBeInTheDocument();
});
});
Explanation:
- The render(<RegisterForm />) function is used to render the RegisterForm component.
- The act function from @testing-library/react is used to handle asynchronous actions. In this case, it wraps the code that interacts with the form elements.
- screen.getByLabelText and screen.getByText are used to obtain references to form elements by their associated labels or text.
- userEvent.type is used to simulate typing input into form fields.
- fireEvent.click simulates clicking the “REGISTER” button.
- The test checks if the form can be filled out and submitted successfully.
Running Tests
Execute the following command to run your tests:
[ npm test ]
![Running Tests Components | Test React Typescript Component with Jest and RTL Running Tests Components](https://www.ignek.com/wp-content/uploads/2024/02/Running-Tests-Components.webp)
Addressing Issues
If you encounter warnings or errors, ensure you use await with act and fix any selector mismatches.