How to mock the response of node-fetch in typescript using supertest and vitest

How to mock the response of node-fetch in typescript using supertest and vitest

Mocking Node-Fetch Responses in TypeScript Using Supertest and Vitest

In the realm of testing Node.js applications, especially those involving network requests, accurately simulating external API responses is crucial. This ensures reliable unit testing by isolating the code under scrutiny from external dependencies. This blog post will guide you through effectively mocking Node-fetch responses in TypeScript, leveraging the powerful combination of Supertest and Vitest.

Understanding the Need for Mocking

Mocking is a technique commonly employed in software testing to isolate and control the behavior of external components, such as network requests or database interactions. In the context of Node.js applications using node-fetch for API calls, mocking these requests allows us to:

  • Control Response Data: Simulate different responses, including success, failure, and edge cases.
  • Isolate Unit Tests: Ensure tests focus on the specific logic under examination, without relying on external API availability.
  • Improve Test Speed: Avoid the overhead of real network requests, leading to faster test execution.

Setting Up the Test Environment

Before diving into mocking, ensure you have the necessary tools installed:

 npm install --save-dev supertest vitest @types/supertest @types/node-fetch 

Mocking Node-Fetch with Supertest and Vitest

Let's demonstrate how to mock node-fetch responses using Supertest and Vitest within a TypeScript project. Assume we have a simple Express.js application with a route that fetches data from an external API:

1. The Application Route

 import express from "express"; import fetch from "node-fetch"; const app = express(); app.get("/api/data", async (req, res) => { try { const response = await fetch("https://api.example.com/data"); const data = await response.json(); res.json(data); } catch (error) { res.status(500).send("Error fetching data"); } }); export default app; 

2. The Test File

 import request from "supertest"; import { describe, expect, it, vi } from "vitest"; import app from "./app"; describe("API Routes", () => { it("should fetch data from the API", async () => { // Mock the fetch response vi.mock("node-fetch", () => ({ default: jest.fn().mockResolvedValue({ json: () => Promise.resolve({ data: "Mocked Data" }), }), })); const response = await request(app).get("/api/data"); expect(response.status).toBe(200); expect(response.body).toEqual({ data: "Mocked Data" }); }); }); 

In this example, we use Vitest's vi.mock function to replace the node-fetch module with a custom mock. This mock resolves to a function that returns a promise with a predefined JSON response. Supertest, then, makes a request to the app's /api/data endpoint, allowing us to assert the expected response.

3. Handling Different Response Scenarios

We can further enhance the testing by simulating various scenarios, such as API errors or different status codes.

 describe("API Routes", () => { it("should handle API errors", async () => { vi.mock("node-fetch", () => ({ default: jest.fn().mockRejectedValue(new Error("API Error")), })); const response = await request(app).get("/api/data"); expect(response.status).toBe(500); expect(response.text).toContain("Error fetching data"); }); it("should handle different status codes", async () => { vi.mock("node-fetch", () => ({ default: jest.fn().mockResolvedValue({ status: 404, json: () => Promise.resolve({ message: "Not Found" }), }), })); const response = await request(app).get("/api/data"); expect(response.status).toBe(404); expect(response.body).toEqual({ message: "Not Found" }); }); }); 

Comparison: Supertest vs. Jest

While Supertest is a popular choice for testing Express.js applications, it's not the only option. Jest, another renowned testing framework, provides its own methods for mocking and testing HTTP requests. Here's a table comparing Supertest and Jest:

Feature Supertest Jest
Purpose Testing Express.js applications General-purpose JavaScript testing
Mocking Built-in mocking for HTTP requests Requires manual mocking with jest.fn() or jest.mock()
Ease of Use Simple and intuitive API for testing routes More flexibility, but can be more complex for HTTP tests
Community Support Strong community and extensive documentation Large and active community with comprehensive support

The choice between Supertest and Jest often depends on project context and preferences. Supertest excels in testing Express.js applications, while Jest offers broader capabilities for general JavaScript testing.

Testing with Mocking: A Comprehensive Example

To demonstrate how to mock node-fetch with Supertest and Vitest in a more practical scenario, let's imagine a user registration API endpoint. Here's how we would test the registration logic, ensuring successful user creation, handling errors, and mocking the external API call:

1. The Application Route (Registration)

 import express from "express"; import fetch from "node-fetch"; const app = express(); app.post("/api/users", async (req, res) => { const { username, email } = req.body; try { const response = await fetch("https://api.external.com/users", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username, email }), }); if (response.ok) { const data = await response.json(); res.status(201).json(data); } else { res.status(response.status).json({ error: "Registration failed" }); } } catch (error) { res.status(500).send("Error registering user"); } }); export default app; 

2. The Test File (Registration Test)

 import request from "supertest"; import { describe, expect, it, vi } from "vitest"; import app from "./app"; describe("User Registration", () => { it("should successfully register a user", async () => { vi.mock("node-fetch", () => ({ default: jest.fn().mockResolvedValue({ ok: true, status: 201, json: () => Promise.resolve({ message: "User successfully registered", userId: 123, }), }), })); const response = await request(app) .post("/api/users") .send({ username: "testuser", email: "test@example.com" }); expect(response.status).toBe(201); expect(response.body).toEqual({ message: "User successfully registered", userId: 123, }); }); it("should handle API errors", async () => { vi.mock("node-fetch", () => ({ default: jest.fn().mockRejectedValue(new Error("API Error")), })); const response = await request(app) .post("/api/users") .send({ username: "testuser", email: "test@example.com" }); expect(response.status).toBe(500); expect(response.text).toContain("Error registering user"); }); }); 

This example demonstrates how to mock various responses, simulating successful registration, API errors, and error handling within the application code.

Conclusion

Mocking Node-fetch responses with Supertest and Vitest is an essential technique for writing robust and efficient unit tests for Node.js applications. By effectively isolating and controlling external dependencies, you gain valuable insights into your application's behavior, leading to improved code quality and confidence. Remember that using these tools to test your API endpoints will save you time and frustration in the long run, ensuring that your application is well-tested and reliable.

For further exploration of testing techniques and best practices, you might find this article helpful: is there is a way to check server cpu and memory usage after doing performance test using karate framework.


⚡️Ejemplo de VITEST MOCK FETCH (TypeScript) FÁCIL y RÁPIDO | Vitest Mock 2024

⚡️Ejemplo de VITEST MOCK FETCH (TypeScript) FÁCIL y RÁPIDO | Vitest Mock 2024 from Youtube.com

Previous Post Next Post

Formulario de contacto