Writing Tests for React Components in SPFx projects with Jest Testing Framework

Best Practices Jest React SPFx Development Microsoft 365

Reference: Blog Post by pH7x Systems (10/2024)

The following content is taken from the blog post mentioned above.

Minimal path to awesome (setup)

Prerequisites

Note

In order to have a proper testing setup, please refer to this article:Testing SPFx projects with Jest

Necessary mode modules (dependencies)

To write tests for React components, make sure you have installed these two node modules in your project scope (on your local dev machine):

npm install --save-dev @testing-library/react @testing-library/jest-dom

Writing Tests

React component and common testing capabilities (@testing-library/react and @testing-library/jest-dom)

Let’s say we have the HelloWorld React component in our SPFx project:

// src/webparts/helloWorld/components/HelloWorld.tsx

// Define the props for our component
export interface IHelloWorldProps {
  name: string;
}

// Define the component
export const HelloWorld: React.FC<IHelloWorldProps> = (props) => {
  // Render a greeting message
  return <h1>Hello, {props.name}!</h1>;
};

We can write a test for this component like so:

// src/webparts/helloWorld/components/HelloWorld.test.tsx
// or (recommended): tests/webparts/helloWorld/components/HelloWorld.test.tsx

import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect";
import { HelloWorld } from "../HelloWorld";

it("renders hello message", () => {
  // Render the component with React Testing Library's render function
  render(<HelloWorld name="SPFx" />);

  // Check if the rendered component includes the text "Hello, SPFx!"
  expect(screen.getByText("Hello, SPFx!")).toBeInTheDocument();
});

Mocking functions & services

When testing components that have dependencies, we can use Jest’s mocking capabilities.

Tip

It is always a good idea to use mocks sparingly to keep tests focused and maintainable.

For example, if we have the HelloWorld component that uses the IDataService service to fetch data, we could mock this service in our test. This allows us to isolate the behavior of the component from its dependencies:

// src/webparts/helloWorld/components/HelloWorld.tsx

import { IDataService } from "../../../services/IDataService";

// Define the props for our component
export interface IHelloWorldProps {
  dataService: IDataService;
}

// Define the component
export const HelloWorld: React.FC<IHelloWorldProps> = (props) => {
  // Initialize state for storing the fetched data
  const [data, setData] = React.useState(null);

  // Fetch data when the component mounts
  React.useEffect(() => {
    props.dataService.fetchData().then(setData);
  }, [props.dataService]);

  // Render the fetched data
  return <h1>{data}</h1>;
};

In a test, we could create a mock data service and pass it to the HelloWorld component:

// src/webparts/helloWorld/components/HelloWorld.test.tsx
// or (recommended): tests/webparts/helloWorld/components/HelloWorld.test.tsx

import { render, screen, waitFor } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect";
import { HelloWorld } from "../HelloWorld";

it("renders data from the service", async () => {
  // Create a mock data service
  const mockDataService = {
    fetchData: jest.fn().mockResolvedValue("Hello, SPFx!"), // 👈 mock the 'fetchData' call and return mocked values
  };

  // Render the component with the mock data service
  render(<HelloWorld dataService={mockDataService} />);

  // Wait for the Promise to resolve and the component to re-render
  await waitFor(() => screen.getByText("Hello, SPFx!"));

  // Check if the rendered component includes the fetched data
  expect(screen.getByText("Hello, SPFx!")).toBeInTheDocument();

  // Check if the fetchData method of the mock data service was called
  expect(mockDataService.fetchData).toHaveBeenCalled();
});
#BishopTells