Heim > Web-Frontend > js-Tutorial > Hauptteil

Verspotten von Netzwerkanfragen leicht gemacht: Jest und MSW integrieren

WBOY
Freigeben: 2024-09-07 15:01:02
Original
1150 Leute haben es durchsucht

Das Schreiben von Unit-Tests, die das Verspotten oder Stubbing von API-Aufrufen beinhalten, kann sich überwältigend anfühlen – ich war selbst dabei. In diesem Artikel führe ich Sie durch eine einfachere und effizientere Möglichkeit, API-Anfragen während des Testens zu simulieren.
Bevor wir eintauchen, finden Sie hier eine Liste der Tools, die wir verwenden werden:

  • Reagieren
  • React Testing Library
  • Scherz
  • Craco: > Wir werden dies später benötigen, um die Jest-Konfiguration zu erweitern
  • MSW (Scheindienstmitarbeiter)

Machen Sie sich keine Sorgen, wenn Sie nicht mit allem vertraut sind – machen Sie einfach mit und ich begleite Sie durch jeden Schritt.

Hier ist der Github-Link zum Projekt

Übersicht
Wir erstellen eine einfache React-App, die mithilfe der Json Placeholder API eine Liste von Todos abruft und anzeigt. In diesem Projekt schreiben wir Testszenarien, um Folgendes zu überprüfen:

  • Die App zeigt einen Ladestatus an, während die API-Anfrage ausgeführt wird.
  • Es wird eine Fehlermeldung angezeigt, wenn die Anfrage fehlschlägt.
  • Es stellt sicher, dass die Liste der Daten von der API korrekt gerendert wird.
  • Es gibt eine Rückmeldung, wenn die Anfrage erfolgreich ist, gibt aber keine Daten zurück.

Dieser Leitfaden behandelt zwei Hauptansätze;

  1. Die traditionelle Art, API-Aufrufe zu verspotten
  2. Der moderne Ansatz mit MSW (Mock Service Worker)

Lasst uns anfangen!

Um mit der Erstellung der App zu beginnen, befolgen Sie diese Schritte:

1. Erstellen Sie eine neue React-App: Führen Sie den folgenden Befehl aus, um Ihre React-Anwendung zu erstellen:

npx create-react-app msw-example
Nach dem Login kopieren

2. Starten Sie die Anwendung: Navigieren Sie nach der Einrichtung zum Projektordner und führen Sie Folgendes aus:

npm start
Nach dem Login kopieren

3. Installieren Sie die erforderlichen Pakete: Als nächstes installieren Sie @tanstack/react-query, um clientseitige Daten zu verwalten:

npm install @tanstack/react-query
Nach dem Login kopieren

React Query (Tanstack Query) hilft bei der serverseitigen Statusverwaltung, einschließlich Caching, Synchronisierung und Datenabruf. Mehr darüber erfahren Sie in der offiziellen Dokumentation

Sie können jetzt mit dem Schreiben der App-Logik beginnen und React Query einrichten, um den Datenabruf effizient zu verwalten. So sieht es nach der Einrichtung aus.

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;
Nach dem Login kopieren

Als nächstes erstellen wir die Todos-Komponente, die eine Liste unserer Todos darstellt.

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>
  );
}

Nach dem Login kopieren

Da nun alles eingerichtet ist, beginnen wir mit dem Schreiben der Tests für die zuvor beschriebenen Szenarien. Zuerst implementieren wir dies mit dem traditionellen Ansatz, mit dem wir bereits vertraut sind – Verspotten von API-Aufrufen mit Jest

Schauen Sie sich das Github-Repo an, damit Sie mitmachen können

Die traditionelle Art, API-Aufrufe zu verspotten

React und Jest arbeiten sofort nahtlos zusammen, sodass keine zusätzliche Konfiguration oder Einrichtung erforderlich ist – zumindest im Moment. Wir erstellen eine Datei mit dem Namen Todos.test.js neben unserer Todo.js-Komponente, in die wir die Todos-Komponente importieren und unsere Tests schreiben.

Wir haben eine Funktion namens getTodos, die dafür verantwortlich ist, einen API-Aufruf durchzuführen, um die Liste der Aufgaben abzurufen und die Antwort zurückzugeben.

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();
}


Nach dem Login kopieren

In Ihrer Todos.test.js-Datei müssen Sie die Todos-Komponente importieren und eine Dienstprogrammfunktion erstellen, die einen Wrapper mit dem React Query-Anbieter bereitstellt. Dadurch wird sichergestellt, dass die Todos-Komponente und ihre untergeordneten Komponenten React-Query zum Verwalten des Serverstatus in Tests verwenden können.

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();

Nach dem Login kopieren

Als nächstes müssen wir die Funktion getTodos verspotten. Dadurch können wir die Rückgabewerte für jedes Testszenario angeben und haben so die Kontrolle über die Daten, die die Funktion während des Tests zurückgibt. Darüber hinaus stellen wir sicher, dass alle verbleibenden Daten aus früheren Testfällen bereinigt werden, sodass jeder Testfall mit einem sauberen Blatt beginnt.

Codebeispiel

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

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

Nach dem Login kopieren

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();
});

Nach dem Login kopieren

Erstes Testszenario: Rendert den Ladezustand
Wir möchten überprüfen, ob unsere Komponente den Ladestatus korrekt anzeigt, während die Anfrage ausgeführt wird.

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();
});
Nach dem Login kopieren

Zweites Testszenario: Rendert den Fehlerstatus, wenn die Anfrage fehlschlägt oder ein Netzwerkfehler vorliegt
Wir möchten überprüfen, ob die Komponente einen Fehlerstatus korrekt wiedergibt, wenn die API-Anfrage fehlschlägt oder ein Netzwerkfehler auftritt.

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();
});
Nach dem Login kopieren

Drittes Testszenario: Rendert die Liste der Aufgaben
Wir möchten überprüfen, ob unsere Komponente die Aufgabenliste korrekt wiedergibt, wenn die Anfrage erfolgreich ist.

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);
});
Nach dem Login kopieren

Viertes Testszenario: Rendert die Liste der Aufgaben
Wir möchten überprüfen, ob Ihre Komponente eine Feedback-Nachricht korrekt wiedergibt, wenn die API-Anfrage eine leere Liste von Aufgaben zurückgibt.

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();
});
Nach dem Login kopieren

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
Nach dem Login kopieren

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/
Nach dem Login kopieren

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();

Nach dem Login kopieren

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
Nach dem Login kopieren

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>
);

Nach dem Login kopieren

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
Nach dem Login kopieren

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();

Nach dem Login kopieren

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" },
    ]);
  }),
];

Nach dem Login kopieren

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);
Nach dem Login kopieren

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);

Nach dem Login kopieren

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());
Nach dem Login kopieren

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();
});
Nach dem Login kopieren

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();
});

Nach dem Login kopieren

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);
});
Nach dem Login kopieren

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();
});
Nach dem Login kopieren

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! ?

Das obige ist der detaillierte Inhalt vonVerspotten von Netzwerkanfragen leicht gemacht: Jest und MSW integrieren. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!