Maison > interface Web > js tutoriel > le corps du texte

Se moquer des requêtes réseau en toute simplicité : intégration de Jest et MSW

WBOY
Libérer: 2024-09-07 15:01:02
original
1153 Les gens l'ont consulté

Écrire des tests unitaires qui impliquent des appels d'API moqueurs ou stubbants peut sembler écrasant : j'y suis moi-même allé. Dans cet article, je vais vous guider à travers une manière plus simple et plus efficace de simuler les requêtes API pendant les tests.
Avant de plonger dans le vif du sujet, voici une liste d’outils que nous utiliserons :

  • Réagir
  • Bibliothèque de tests React
  • Blague
  • Craco :> nous en aurons besoin pour étendre la configuration de Jest plus tard
  • MSW (travailleur de service simulé)

Ne vous inquiétez pas si vous ne connaissez pas tout, suivez simplement et je vous guiderai à travers chaque étape.

Voici le lien Github vers le projet

Aperçu
Nous allons créer une application React simple qui récupère et affiche une liste de tâches à l'aide de l'API Json Placeholder. Dans ce projet, nous rédigerons des scénarios de test pour vérifier les éléments suivants :

  • L'application affiche un état de chargement pendant que la requête API est en cours.
  • Il affiche un message d'erreur si la demande échoue.
  • Il garantit que la liste des données de l'API est correctement restituée.
  • Il fournit un retour lorsque la demande aboutit mais ne renvoie aucune donnée.

Ce guide couvrira deux approches principales ;

  1. La manière traditionnelle de se moquer des appels API
  2. L'approche moderne utilisant MSW (Mock Service Worker)

Commençons !

Pour commencer à créer l'application, suivez ces étapes :

1. Créez une nouvelle application React : Exécutez la commande suivante pour créer votre application React :

npx create-react-app msw-example
Copier après la connexion

2. Démarrez l'application : Après la configuration, accédez au dossier du projet et exécutez :

npm start
Copier après la connexion

3. Installez les packages nécessaires : Ensuite, installez @tanstack/react-query pour gérer les données côté client :

npm install @tanstack/react-query
Copier après la connexion

React Query (Tanstack Query) aide à gérer la gestion de l'état côté serveur, y compris la mise en cache, la synchronisation et la récupération de données. Vous pouvez en savoir plus dans leur documentation officielle

Vous pouvez maintenant commencer à écrire la logique de l'application et configurer React Query pour gérer efficacement la récupération des données. Voici à quoi cela ressemble après l'avoir configuré.

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

import { Todos } from "components/Todos";

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <div data-testid="app" className="App">
        <Todos />
      </div>
    </QueryClientProvider>
  );
}

export default App;
Copier après la connexion

Ensuite, nous créons le composant Todos qui affiche une liste de nos tâches.

Todos.js

import { useQuery } from "@tanstack/react-query";

import { getTodos } from "api/todo";

export function Todos() {
  const { data, isError, isLoading } = useQuery({
    queryFn: getTodos,
    queryKey: ["TODOS"],
  });

  if (isLoading) {
    return <p>loading todo list</p>;
  }

  if (isError) {
    return <p>an error occurred fetching todo list</p>;
  }

  return (
    <div>
      {Boolean(data.length) ? (
        <ol>
          {data.map((item) => (
            <li key={item.id}>{item.title}</li>
          ))}
        </ol>
      ) : (
        <p>You do not have todos created yet</p>
      )}
    </div>
  );
}

Copier après la connexion

Maintenant que tout est configuré, commençons à écrire les tests pour les scénarios que nous avons décrits plus tôt. Tout d’abord, nous allons implémenter cela en utilisant l’approche traditionnelle que nous connaissons déjà : nous moquer des appels d’API à l’aide de Jest

Consultez le dépôt github pour pouvoir suivre

La manière traditionnelle de se moquer des appels API

React et Jest fonctionnent ensemble de manière transparente, aucune configuration ou configuration supplémentaire n'est donc nécessaire, du moins pour le moment. Nous allons créer un fichier nommé Todos.test.js à côté de notre composant Todo.js, où nous importerons le composant Todos et rédigerons nos tests..

Nous avons une fonction appelée getTodos qui est chargée de faire un appel API pour récupérer la liste des tâches et renvoyer la réponse.

export async function getTodos() {
  const response = await fetch("https://jsonplaceholder.typicode.com/todos", {
    headers: {
      "Content-Type": "application/json",
    },
  });

  if (!response.ok) {
    const res = await response.json();
    throw new Error(res.message || response.status);
  }

  return response.json();
}


Copier après la connexion

Dans votre fichier Todos.test.js, vous devez importer le composant Todos et créer une fonction utilitaire qui fournit un wrapper avec le fournisseur React Query. Cela garantit que le composant Todos et ses enfants peuvent utiliser React-Query pour gérer l'état du serveur dans les tests.

import { render, screen, waitFor, within } from "@testing-library/react";

import { Todos } from "./Todos";
import { reactQueryWrapper } from "utils/reactQueryWrapper";
import { getTodos } from "api/todo";

const { wrapper, queryCache } = reactQueryWrapper();

Copier après la connexion

Ensuite, nous devons nous moquer de la fonction getTodos. Cela nous permettra de spécifier les valeurs de retour pour chaque scénario de test, nous donnant ainsi le contrôle des données renvoyées par la fonction pendant le test. De plus, nous veillerons à ce que toutes les données restantes des scénarios de test précédents soient nettoyées, afin que chaque scénario de test démarre sur une table rase.

Exemple de code

jest.mock("api/todo", () => ({
  getTodos: jest.fn(),
}));

afterEach(() => {
  queryCache.clear();
  jest.clearAllMocks();
});

Copier après la connexion

Todos.test.js

import { render, screen, waitFor, within } from "@testing-library/react";

import { Todos } from "./Todos";
import { reactQueryWrapper } from "utils/reactQueryWrapper";
import { getTodos } from "api/todo";

const { wrapper, queryCache } = reactQueryWrapper();

jest.mock("api/todo", () => ({
  getTodos: jest.fn(),
}));

afterEach(() => {
  queryCache.clear();
});

afterEach(() => {
  jest.clearAllMocks();
});

Copier après la connexion

Premier scénario de test : Rend l'état de chargement
Nous souhaitons vérifier que notre composant affiche correctement l'état de chargement pendant que la requête est en cours.

test("Renders loading state", () => {
  getTodos.mockImplementation(() => {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve();
      }, 1000);
    });
  });

  render(<Todos />, { wrapper });
  const loadingText = screen.getByText("loading todo list");
  expect(loadingText).toBeInTheDocument();
});
Copier après la connexion

Deuxième scénario de test : Rend l'état d'erreur lorsque la demande échoue ou en cas d'erreur réseau
Nous voulons vérifier que le composant affiche correctement un état d'erreur lorsque la requête API échoue ou rencontre une erreur réseau.

test("Renders error state when request fails or there is network error", async () => {
  getTodos.mockImplementationOnce(() => {
    return new Promise((resolve, reject) => {
      reject();
    });
  });

  render(<Todos />, { wrapper });
  await screen.findByText("an error occurred fetching todo list");
  expect(
    screen.getByText("an error occurred fetching todo list")
  ).toBeInTheDocument();
});
Copier après la connexion

Troisième scénario de test : Liste des rendus des tâches
Nous voulons vérifier que notre composant restitue correctement la liste des tâches lorsque la requête réussit.

test("Renders list of todos", async () => {
  getTodos.mockImplementation(() => {
    return Promise.resolve([
      { id: 1, title: "Exercise" },
      { id: 2, title: "Cook" },
    ]);
  });

  render(<Todos />, { wrapper });

  const loadingText = screen.queryByText("loading todo list");
  await waitFor(() => expect(loadingText).not.toBeInTheDocument());
  const list = screen.getByRole("list");
  expect(list).toBeInTheDocument();
  expect(within(list).getAllByRole("listitem")).toHaveLength(2);
});
Copier après la connexion

Quatrième scénario de test : Liste des rendus des tâches
Nous souhaitons vérifier que votre composant affiche correctement un message de retour lorsque la requête API renvoie une liste vide de tâches.

test("Renders feedback message when user has an empty list of todos", async () => {
  getTodos.mockImplementationOnce(() => {
    return Promise.resolve([]);
  });

  render(<Todos />, { wrapper });

  await waitFor(() =>
    expect(screen.queryByText("loading todo list")).not.toBeInTheDocument()
  );
  expect(
    screen.getByText("You do not have todos created yet")
  ).toBeInTheDocument();
});
Copier après la connexion

Great! Now that we've covered mocking API calls with Jest, let’s explore a better approach using Mock Service Worker (MSW). MSW provides a more elegant and maintainable way to mock API calls by intercepting network requests at the network level rather than within your tests.

Introducing MSW (Mock Service Worker)

Mock Service Worker (MSW) is an API mocking library designed for both browser and Node.js environments. It allows you to intercept outgoing requests, observe them, and provide mocked responses. MSW helps you simulate real-world scenarios in your tests, making them more robust and reliable.

Read more about MSW

Setting Up MSW

Step 1: Install MSW using the following command:

npm install msw@latest --save-dev
Copier après la connexion

Step 2: Set up the environment you wish to intercept requests in—either Browser or Node. Before doing so, create a mock directory within your src directory. Inside this directory, you'll create the following files and directories:

browser.js: Handles request interception in the browser environment.
server.js: Handles request interception in the Node.js environment.
handlers: A directory containing files that define the API endpoints to intercept.

Here’s how your folder structure should look:

src/
  └── mock/
      ├── browser.js
      ├── server.js
      └── handlers/
Copier après la connexion

This setup ensures that you have a clear organization for intercepting and handling requests in both browser and Node.js environments using MSW.

Browser Environment Setup

To set up MSW for intercepting requests in the browser, follow these steps:

1. Create the browser.js File

In your src/mock directory, create a file named browser.js. This
file will set up the MSW worker to intercept requests in the
browser environment.

// src/mock/browser.js

import { setupWorker } from 'msw/browser';

// Create a worker instance to intercept requests
export const worker = setupWorker();

Copier après la connexion

2. Generate the mockServiceWorker.js File
This file is required for MSW to function properly in the browser.
Generate it using the following command from the root directory of
your application:

npx msw init <PUBLIC_DIR> --save
Copier après la connexion

This command initializes the MSW service worker and places the mockServiceWorker.js file into the public directory of your React app.

3. Start the Service Worker

Import and start the worker in your application entry point
(typically index.js or App.js).

// src/index.js

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";

if (process.env.NODE_ENV === "development") {
  const mswState = localStorage.getItem("mswState") === "enabled";
  if (mswState) {
    const { worker } = require("./mocks/browser");
    worker.start();
    window.__mswStop = worker.stop;
  }
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

Copier après la connexion

4. Verify the Setup
To ensure that the service worker is correctly set up, navigate to
the URL of your application in the browser:

http://localhost:3000/mockServiceWorker.js
Copier après la connexion

You should see the service worker script displayed in your browser. This confirms that the service worker is correctly installed and ready to intercept requests.

If your MSW setup is correct and enabled, you should see a console message indicating that MSW is active. Your browser console should display logs similar to this:

Mocking Network Requests Made Easy: Integrating Jest and MSW

These logs confirm that the MSW service worker is properly intercepting network requests and is ready to mock API responses according to the handlers you’ve defined.

Node Environment Setup

To set up MSW for intercepting requests in a Node.js environment (for example, in server-side tests), follow these steps:

Step 1: Create the server.js File
In the src/mock directory, create a file named server.js. This file sets up the MSW server to intercept requests in a Node.js environment.

// src/mock/server.js

import { setupServer } from "msw/browser";

// Create a server instance with the defined request handlers
export const server = setupServer();

Copier après la connexion

Step 2: Define the API Handlers
Create a file named posts.js in the handlers directory.This file will describe the APIs you want to intercept and the mock responses.

// src/mock/handlers/posts.js

import { http, HttpResponse } from "msw";

export const postHandlers = [

 // Handler for GET /todos request
  http.get("https://jsonplaceholder.typicode.com/todos", () => {
    return HttpResponse.json([
      { id: 1, title: "totam quia non" },
      { id: 2, title: "sunt cum tempora" },
    ]);
  }),
];

Copier après la connexion

Here, we're defining that when MSW intercepts a GET request to https://jsonplaceholder.typicode.com/todos, it should respond with a 200 status code and the provided JSON data.

Step 3: Hook Handlers to the Browser Worker
Update the browser.js file to include the defined handlers.

import { setupWorker } from "msw/browser";

import { postHandlers } from "./handlers/posts";

export const worker = setupWorker(...postHandlers);
Copier après la connexion

Step 4: Hook Handlers to the Node Server
Ensure the handlers are also used in the Node.js environment by updating the server.js file.

import { setupServer } from "msw/node";

import { postHandlers } from "./handlers/posts";

export const server = setupServer(...postHandlers);

Copier après la connexion

With these configurations in place, your MSW setup is complete and ready for both browser and Node.js environments. Congratulations on completing the setup! ?

Using MSW in our Tests
To use MSW in your tests, you need to set up your test environment to utilize the mock server for intercepting API calls. Here’s a guide to setting up and writing tests using MSW with your Todos component.

  1. Create the Test File
    Create a new file named Todos.MSW.test.js next to your
    Todos.jscomponent. This file will contain your tests that
    utilize MSW for mocking API responses.

  2. Set Up Test Environment
    In your Todos.MSW.test.js file, import the necessary modules and
    set up the environment for using MSW with your tests. Below is an
    example setup:

import { render, screen, waitFor, within } from "@testing-library/react";
import { http, delay, HttpResponse } from "msw";

import { Todos } from "./Todos";
import { reactQueryWrapper } from "utils/reactQueryWrapper";
import { server } from "mocks/server";

const { wrapper, queryCache } = reactQueryWrapper();

afterEach(() => {
  queryCache.clear();
});

afterEach(() => {
  jest.clearAllMocks();
});

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
Copier après la connexion

First Test Scenario: Renders loading state
We want to verify that our component correctly displays the loading state while the request is in progress.

test("Renders loading state", () => {
  server.use(
    http.get("https://jsonplaceholder.typicode.com/todos", async () => {
      await delay(1000);
      return HttpResponse.json([]);
    })
  );

  render(<Todos />, { wrapper });
  const loadingText = screen.getByText("loading todo list");
  expect(loadingText).toBeInTheDocument();
});
Copier après la connexion

Second Test Scenario: Renders error state when request fails or there is network error
We want to verify that the component correctly renders an error state when the API request fails or encounters a network error.

test("Renders error state when request fails or there is network error", async () => {
  server.use(
    http.get("https://jsonplaceholder.typicode.com/todos", async () => {
      return HttpResponse.json([], {
        status: 500,
      });
    })
  );

  render(<Todos />, { wrapper });
  await screen.findByText("an error occurred fetching todo list");
  expect(
    screen.getByText("an error occurred fetching todo list")
  ).toBeInTheDocument();
});

Copier après la connexion

Third Test Scenario: Renders list of todos
We want to verify that our component correctly renders the list of todos when the request is successful.

test("Renders list of todos", async () => {

  render(<Todos />, { wrapper });

  const loadingText = screen.queryByText("loading todo list");
  await waitFor(() => expect(loadingText).not.toBeInTheDocument());
  const list = screen.getByRole("list");
  expect(list).toBeInTheDocument();
  expect(within(list).getAllByRole("listitem")).toHaveLength(2);
});
Copier après la connexion

Fourth Test Scenario: Renders list of todos
We want to verify that your component correctly renders a feedback message when the API request returns an empty list of todos.

test("Renders feedback message when user has an empty list of todos", async () => {
   server.use(
    http.get("https://jsonplaceholder.typicode.com/todos", () => {
      return HttpResponse.json([]);
    })
  );
  render(<Todos />, { wrapper });

  await waitFor(() =>
    expect(screen.queryByText("loading todo list")).not.toBeInTheDocument()
  );
  expect(
    screen.getByText("You do not have todos created yet")
  ).toBeInTheDocument();
});
Copier après la connexion

Final Thoughts
Mocking API calls is crucial for effective testing, allowing you to simulate different scenarios without relying on real backend services. While traditional Jest mocking can be effective, MSW offers a more sophisticated solution with better support for various environments and more realistic request handling.

Here are a few tips to keep in mind:

  • Choose the Right Tool: Use MSW for a more comprehensive
    solution that integrates seamlessly with your React application,
    especially when dealing with complex request handling.

  • Organize Your Handlers: Keep your API handlers well-organized
    and modular to make them easier to maintain and extend.

  • Clean Up After Tests: Ensure that your tests clean up properly
    by resetting handlers and clearing mocks to avoid interference
    between tests.

  • Verify Setup: Always check your setup by inspecting the network
    requests and responses to ensure that everything is working as
    expected.
    By incorporating MSW into your testing strategy, you'll achieve more reliable and maintainable tests, leading to a smoother development experience and higher quality software.

Happy testing! ?

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!