DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • How to Build a Pokedex React App with a Slash GraphQL Backend
  • Validation Forms in Vue.js Apps Using Vuelidate library
  • The Cypress Edge: Next-Level Testing Strategies for React Developers
  • Top React Libraries for Data-Driven Dashboard App Development

Trending

  • Revolutionizing Financial Monitoring: Building a Team Dashboard With OpenObserve
  • Intro to RAG: Foundations of Retrieval Augmented Generation, Part 1
  • It’s Not About Control — It’s About Collaboration Between Architecture and Security
  • How the Go Runtime Preempts Goroutines for Efficient Concurrency
  1. DZone
  2. Data Engineering
  3. Data
  4. How to Add Test Cases to Your React Typescript App

How to Add Test Cases to Your React Typescript App

This blog will help you learn the React Testing Library and how to use it to test your React application.

By 
Kiran Beladiya user avatar
Kiran Beladiya
·
Jan. 18, 23 · Analysis
Likes (1)
Comment
Save
Tweet
Share
3.8K Views

Join the DZone community and get the full member experience.

Join For Free

React Testing Library is a testing utility tool built to test the actual DOM tree rendered by React on the browser. The goal of the library is to help you write tests that resemble how a user would use your application. This can give you more confidence that your application works as intended when a real user does use it.

React Testing Library Methods for Finding Elements

Most of your React test cases should use methods for finding elements. React Testing Library provides several methods to find an element by specific attributes in addition to the getByText() method.

  • getByText(): find the element by its textContent value
  • getByRole(): by its role attribute value
  • getByLabelText(): by its label attribute value
  • getByPlaceholderText(): by its placeholder attribute value
  • getByAltText(): by its alt attribute value
  • getByDisplayValue(): by its value attribute, usually for elements
  • getByTitle(): by its title attribute value

Three Blocks of Test Cases (AAA)

1. Arrange

The render method renders a React element into the DOM.

 
render(<Fetch url="/greeting" />)


2. Act

The fireEvent method allows you to fire events to simulate user actions.

 
await act(async () => {
      fireEvent.click(RegisterButton);
    });


3. Assert 

A custom matcher from jest-dom.

 
expect(getByTestId('firstName')).toBeInTheDocument();


Steps to Add Tests

Step 1: Install React Testing Library

  • We are using the below-linked project as a base for writing test cases.

You can refer to the link.

  • You can add react testing library via npm and Yarn like:
 
npm install --save-dev @testing-library/react
                OR
yarn add --dev @testing-library/react


Step 2: Getting Started

  • 'npm start': Starts the development server.
  • 'npm run build': Bundles the app into static files for production.
  • 'npm test': Starts the test runner.

Step 3: Optional: Add the React Code to the Project

  • Creating the form:
 
import React from 'react';
import { Formik, Field, Form } from 'formik';
import * as Yup from 'yup';
import 'bootstrap/dist/css/bootstrap.min.css';
import './FormTable.css';

interface Values {
  firstName: string;
  lastName: string;
  email: string;
  password: string;
  confirmPassword: string;
}

const FormTable = () => {
  const validateEmail = (value: any) => {
    let error;
    if (!value) {
      error = 'please fill the details';
    } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)) {
      error = 'Invalid email address';
    }
    return error;
  };

  const SignUpSchema = Yup.object().shape({
    firstName: Yup.string()
      .min(5, 'Should be 5 character long')
      .max(15, 'should not exceed 15 characters')
      .required('FirstName is required'),
    lastName: Yup.string()
      .min(5, 'Should be 5 character long')
      .max(15, 'should not exceed 15 characters')
      .required('LastName is required'),
    email: Yup.string()
      .email('invalid email address')
      .required('Email is required'),
    password: Yup.string()
      .required('Password is required')
      .min(6, 'Password must be at least 6 characters')
      .max(40, 'Password must not exceed 40 characters'),
    confirmPassword: Yup.string()
      .required('Confirm Password is required')
      .oneOf([Yup.ref('password'), null], 'Your Password does not match'),
  });

  return (
    <div data-testid="sign-up-form">
      <Formik
        initialValues={{
          firstName: '',
          lastName: '',
          email: '',
          password: '',
          confirmPassword: '',
        }}
        validationSchema={SignUpSchema}
        onSubmit={(values: Values) => {
          console.log('values', values);
        }}
      >
        {({ errors, touched, values }) => (
          <>
            <section>
              <div className="mask d-flex align-items-center h-100 gradient-custom-3">
                <div className="container h-100">
                  <div
                    className="row d-flex justify-content-center align-items-center h-100"
                    style={{ marginTop: '55px', marginBottom: '55px' }}
                  >
                    <div className="col-12 col-md-9 col-lg-7 col-xl-6">
                      <div className="card" style={{ borderRadius: '15px' }}>
                        <div className="card-body p-5">
                          <h2 className="text-uppercase text-center mb-5">
                            Create an account
                          </h2>
                          <Form>
                            <div
                              className="form-outline mb-4"
                              data-testid="firstName"
                            >
                              <label className="mb-2">First Name</label>
                              <Field
                                name="firstName"
                                type="text"
                                className="form-control pl-2"
                                placeholder="First Name"
                                value={values.firstName}
                              />
                              {errors.firstName && touched.firstName && (
                                <div
                                  className="text-danger"
                                  data-testid="error-firstName"
                                >
                                  {errors.firstName}
                                </div>
                              )}
                            </div>
                            <div
                              className="form-outline mb-4"
                              data-testid="lastName"
                            >
                              <label className="mb-2">Last Name</label>
                              <Field
                                name="lastName"
                                type="text"
                                className="form-control pl-2"
                                placeholder="Last Name"
                                value={values.lastName}
                              />

                              {errors.lastName && touched.lastName && (
                                <div
                                  className="text-danger"
                                  data-testid="error-lastName"
                                >
                                  {errors.lastName}
                                </div>
                              )}
                            </div>

                            <div
                              className="form-outline mb-4"
                              data-testid="password"
                            >
                              <label className="mb-2">Password</label>
                              <Field
                                name="password"
                                type="password"
                                className="form-control pl-2"
                                value={values.password}
                              />
                              {errors.password && touched.password && (
                                <div
                                  className="text-danger"
                                  data-testid="error-password"
                                >
                                  {errors.password}
                                </div>
                              )}
                            </div>

                            <div
                              className="form-outline mb-4"
                              data-testid="confirmPassword"
                            >
                              <label className="mb-2">Confirm Password</label>
                              <Field
                                autoComplete="on"
                                name="confirmPassword"
                                type="password"
                                className="form-control pl-2"
                                value={values.confirmPassword}
                              />
                              {errors.confirmPassword &&
                                touched.confirmPassword && (
                                  <div
                                    className="text-danger"
                                    data-testid="error-confirmPassword"
                                  >
                                    {errors.confirmPassword}
                                  </div>
                                )}
                            </div>

                            <div
                              className="form-outline mb-4"
                              data-testid="email"
                            >
                              <label className="mb-2"> Email </label>
                              <Field
                                name="email"
                                type="email"
                                value={values.email}
                                data-testid="emailAddress"
                                validate={validateEmail}
                                placeholder="john@example.com"
                                className="form-control pl-2"
                              />
                              {errors.email && touched.email && (
                                <div
                                  className="text-danger"
                                  data-testid="error-email"
                                >
                                  {errors.email}
                                </div>
                              )}
                            </div>

                            <div className="form-check d-flex justify-content-center mb-5">
                              <input
                                className="form-check-input me-2"
                                type="checkbox"
                                value=""
                                id="form2Example3cg"
                              />
                              <label
                                className="form-check-label"
                                htmlFor="form2Example3g"
                              >
                                I agree all statements in{' '}
                                <a href="#!" className="text-body">
                                  <u>Terms of service</u>
                                </a>
                              </label>
                            </div>

                            <div className="d-flex justify-content-center">
                              <button
                                type="submit"
                                data-testid="Register-target-btn"
                                className="btn btn-success btn-block btn-lg gradient-custom-4 text-body"
                              >
                                Register
                              </button>
                            </div>

                            <p className="text-center text-muted mt-5 mb-0">
                              Have already an account?{' '}
                              <a href="#!" className="fw-bold text-body">
                                <u>Login here</u>
                              </a>
                            </p>
                          </Form>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </section>
          </>
        )}
      </Formik>
    </div>
  );
};
export default FormTable;


  • Create the Form with Formik.
  • Add Validation with Yup.
  • Custom Validation Rules.
  • On submit form event, display the form element value in the card component.

Step 4: Add Command for Running Test Cases in the Package JSON File

 
"test": "react-scripts test"

 // It will run all file test cases
 "test: all": "react-scripts test --watchAll=false -u"

 // It will check the coverage of the file
 "test: coverage": "test: all --coverage"


Step 5: Add Test Cases

  • Create a FormTable folder inside the src folder.
  • Create a __ tests__ folder inside the FormTable folder.
  • Create Formtable.test.tsx file inside the __ tests__ folder for writing test cases for react code.
 
import { fireEvent, render, screen } from '@testing-library/react';
import { createRenderer } from 'react-test-renderer/shallow';
import FormTable from '../FormTable';
import { act } from 'react-test-renderer';

const renderer = createRenderer();

const defaultComponent = <FormTable />;

describe('<App />', () => {
  it('should render and match the snapshot', () => {
    renderer.render(defaultComponent);
    const renderedOutput = renderer.getRenderOutput();
    expect(renderedOutput).toMatchSnapshot();
  });

  it('should have correct label', () => {
    const { getByTestId } = render(defaultComponent);
    expect(getByTestId('sign-up-form')).toBeTruthy();
  });

  it('Register button is clickable', async () => {

    // Arrange
    const { getByTestId } = render(defaultComponent);
    const RegisterButton = getByTestId('Register-target-btn');

    // Act
    await act(async () => {
      fireEvent.click(RegisterButton);
    });

    // Assert
    expect(getByTestId('Register-target-btn')).toBeVisible();
  });

  it('A valid form data submit', async () => {
    const { getByTestId } = render(defaultComponent);
    const RegisterButton = getByTestId('Register-target-btn');

    await act(async () => {
      fireEvent.click(RegisterButton);
    });

    expect(getByTestId('firstName')).toBeInTheDocument();
    expect(getByTestId('lastName')).toBeInTheDocument();
    expect(getByTestId('email')).toBeInTheDocument();
    expect(getByTestId('password')).toBeInTheDocument();
    expect(getByTestId('confirmPassword')).toBeInTheDocument();
  });

  it('A validation message on form data Register', async () => {
    const { getByTestId } = render(defaultComponent);
    const RegisterButton = getByTestId('Register-target-btn');

    await act(async () => {
      fireEvent.click(RegisterButton);
    });

    expect(getByTestId('error-firstName')).toHaveTextContent(
      'FirstName is required',
    );
    expect(getByTestId('error-lastName')).toHaveTextContent(
      'LastName is required',
    );
    expect(getByTestId('error-password')).toHaveTextContent(
      'Password is required',
    );
    expect(getByTestId('error-confirmPassword')).toHaveTextContent(
      'Confirm Password is required',
    );
    expect(getByTestId('error-email')).toHaveTextContent('Email is required');
  });

  it('Check the email value', async () => {
    const { getByTestId } = render(defaultComponent);
    const RegisterButton = getByTestId('Register-target-btn');
    const emailField = screen.getByTestId('emailAddress');

    await act(async () => {
      fireEvent.change(emailField, {
        target: { value: 'test00hfhdhhfgmailco' },
      });

      RegisterButton.dispatchEvent(new Event('submit'));
    });

    expect(getByTestId('emailAddress')).toBeTruthy();
  });
});


How to Write Test Cases for React Project

  • Render the snapshot for the form component using the toMatchSnapshot, except for the method of testing the library. 'toMatchSnapshot': This ensures that a value matches the most recent snapshot.
 
describe('<App />', () => {
  it('should render and match the snapshot', () => {
    renderer.render(defaultComponent);
    const renderedOutput = renderer.getRenderOutput();
    expect(renderedOutput).toMatchSnapshot();
  });
});


  • Add data-test id in HTMLElement to check the element is present in the code. To provide a unique data-test id attribute for each component. 'data-test id': It is an attribute used to identify a DOM node for testing purposes. It should use as a handler for the test code. Therefore, we need to make sure its value is unique.

 'data-test id': It is an attribute used to identify a DOM node for testing purposes.

 
it('should have correct label', () => {
    const { getByTestId } = render(defaultComponent);
    expect(getByTestId('sign-up-form')).toBeTruthy();
  });


  • Write test cases on getByTestId using data-test id.

For example:

data-test id='sign-up form'

expect(getByTestId('sign-up form')).toBeTruthy();

We can use different expected methods for test cases.

  • Write test cases on submit button click using fireEvent.
 
it('Register button is clickable', async () => {
    const { getByTestId } = render(defaultComponent);
    const RegisterButton = getByTestId('Register-target-btn');

    await act(async () => {
      fireEvent.click(RegisterButton);
    });

    expect(getByTestId('Register-target-btn')).toBeVisible();
  });


  • Write test cases to check valid form data on submit button.
 
it('A valid form data submit', async () => {
    const { getByTestId } = render(defaultComponent);
    const RegisterButton = getByTestId('Register-target-btn');

    await act(async () => {
      fireEvent.click(RegisterButton);
    });

    expect(getByTestId('firstName')).toBeInTheDocument();
    expect(getByTestId('lastName')).toBeInTheDocument();
    expect(getByTestId('email')).toBeInTheDocument();
    expect(getByTestId('password')).toBeInTheDocument();
    expect(getByTestId('confirmPassword')).toBeInTheDocument();
  });


  • Write test cases to validate messages on form data submitted.
 
it('A validation message on form data Register', async () => {
    const { getByTestId } = render(defaultComponent);
    const RegisterButton = getByTestId('Register-target-btn');

    await act(async () => {
      fireEvent.click(RegisterButton);
    });

    expect(getByTestId('error-firstName')).toHaveTextContent(
      'FirstName is required',
    );
    expect(getByTestId('error-lastName')).toHaveTextContent(
      'LastName is required',
    );
    expect(getByTestId('error-password')).toHaveTextContent(
      'Password is required',
    );
    expect(getByTestId('error-confirmPassword')).toHaveTextContent(
      'Confirm Password is required',
    );
    expect(getByTestId('error-email')).toHaveTextContent('Email is required');
  });


  • Write test cases to check the email value.
 
it('Check the email value', async () => {
    const { getByTestId } = render(defaultComponent);
    const RegisterButton = getByTestId('Register-target-btn');
    const emailField = screen.getByTestId('emailAddress');

    await act(async () => {
      fireEvent.change(emailField, {
        target: { value: 'test00hfhdhhfgmailco' },
      });

      RegisterButton.dispatchEvent(new Event('submit'));
    });

    expect(getByTestId('emailAddress')).toBeTruthy();
  });


Step 6: Edit Your Package JSON File for Some Rules for React Testing Library (coveragePathIgnorePatterns [array])

 
"jest": {
    "coveragePathIgnorePatterns": [
      "src/index.tsx",
      "src/reportWebVitals.ts"
    ]
  }


  • coveragePathIgnorePatterns: That matched against all file paths before executing the test. Coverage information will skip if the file path matches any patterns.

Step 7: Check the Test Cases Coverage

  • Run the yarn run test: all --coverage command.

Run the yarn run test: all --coverage command.

Output 

Conclusion

You're done with the code.

We hope your code is working as expected. To make your task even easier, we have created GitHub Repo. You can try the running demo app.

app Data (computing) Form (document) React (JavaScript library) Testing Data Types

Published at DZone with permission of Kiran Beladiya. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • How to Build a Pokedex React App with a Slash GraphQL Backend
  • Validation Forms in Vue.js Apps Using Vuelidate library
  • The Cypress Edge: Next-Level Testing Strategies for React Developers
  • Top React Libraries for Data-Driven Dashboard App Development

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!