You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js 14使用Jest测试API调用时遇TextEncoder未定义错误

在Next.js 14中用Jest测试API调用时解决ReferenceError: TextEncoder is not defined问题

我在Next.js 14项目中首次使用Jest测试apiCalls.js里的getRanking、createRanking、updateRanking三个API方法,已经配置了jest.config.js和jestconfig.json,但运行测试时抛出ReferenceError: TextEncoder is not defined错误,修改配置后问题依旧,求解决办法。

当前配置文件

jest.config.js

const nextJest = require("next/jest");

/** @type {import('jest').Config} */
const createJestConfig = nextJest({
  // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
  dir: "./",
});

// Add any custom config to be passed to Jest
const config = {
  coverageProvider: "v8",
  testEnvironment: "jsdom",
  moduleNameMapper: {
    "^@/components/(.*)$": "<rootDir>/components/$1",
    "^@/lib/(.*)$": "<rootDir>/lib/$1",
  },
};

// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(config);

jestconfig.json

{
  "compilerOptions": {
    "module": "esnext",
    "moduleResolution": "bundler",
    "baseUrl": "./",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

测试文件代码

import { getRanking, createRanking, updateRanking } from "@/lib/apiCalls";

describe("getRanking function", () => {
  beforeEach(() => {
    fetchMock.mockClear();
  });

  it("fetches data successfully", async () => {
    const mockData = [{ id: 1, name: "Player A", score: 100 }];
    fetchMock.mockResolvedValueOnce({
      json: async () => mockData,
    });

    const result = await getRanking();
    expect(result).toEqual(mockData[0]);
    expect(fetchMock).toHaveBeenCalledTimes(1);
    expect(fetchMock).toHaveBeenCalledWith(
      process.env.NEXT_PUBLIC_RANKING_API_URL,
      {
        method: "GET",
        headers: {
          "content-type": "application/json",
        },
      }
    );
  });

  it("handles errors", async () => {
    fetchMock.mockRejectedValueOnce(new Error("API is down"));

    const result = await getRanking();
    expect(result).toBeUndefined();
    expect(console.log).toHaveBeenCalledWith(new Error("API is down"));
  });
});

describe("createRanking function", () => {
  beforeEach(() => {
    fetchMock.mockClear();
  });

  it("creates ranking successfully", async () => {
    fetchMock.mockResolvedValueOnce({
      json: async () => ({ success: true }),
    });

    await createRanking();
    expect(fetchMock).toHaveBeenCalledTimes(1);
    expect(fetchMock).toHaveBeenCalledWith(
      process.env.NEXT_PUBLIC_RANKING_API_URL,
      {
        method: "POST",
        headers: {
          "content-type": "application/json",
        },
      }
    );
  });

  it("handles errors", async () => {
    fetchMock.mockRejectedValueOnce(new Error("Failed to create ranking"));

    await createRanking();
    expect(console.log).toHaveBeenCalledWith(
      new Error("Failed to create ranking")
    );
  });
});

describe("updateRanking function", () => {
  beforeEach(() => {
    fetchMock.mockClear();
  });

  it("updates ranking successfully", async () => {
    const mockWinner = "Player B";
    const mockRanking = [{ id: 1, name: "Player A", score: 100 }];

    fetchMock.mockResolvedValueOnce({
      json: async () => ({ success: true }),
    });

    await updateRanking(mockWinner, mockRanking);
    expect(fetchMock).toHaveBeenCalledTimes(1);
    expect(fetchMock).toHaveBeenCalledWith(
      process.env.NEXT_PUBLIC_RANKING_API_URL,
      {
        method: "PUT",
        body: JSON.stringify({
          winner: mockWinner,
          ranking: mockRanking,
        }),
        headers: {
          "content-type": "application/json",
        },
      }
    );
  });

  it("handles errors", async () => {
    const mockWinner = "Player B";
    const mockRanking = [{ id: 1, name: "Player A", score: 100 }];

    fetchMock.mockRejectedValueOnce(new Error("Failed to update ranking"));

    await updateRanking(mockWinner, mockRanking);
    expect(console.log).toHaveBeenCalledWith(
      new Error("Failed to update ranking")
    );
  });
});

待测试的apiCalls.js代码

import mongoose from "mongoose";

export const getRanking = async () => {
  try {
    const res = await fetch(process.env.NEXT_PUBLIC_RANKING_API_URL, {
      method: "GET",
      headers: {
        "content-type": "application/json",
      },
    });
    const data = await res.json();
    if (data) return data[0];
    return;
  } catch (error) {
    console.log(error);
  }
};

export const createRanking = async () => {
  try {
    const res = await fetch(process.env.NEXT_PUBLIC_RANKING_API_URL, {
      method: "POST",
      headers: {
        "content-type": "application/json",
      },
    });
    const data = await res.json();
  } catch (error) {
    console.log(error);
  }
};

export const updateRanking = async (winner, ranking) => {
  const args = {
    winner: winner,
    ranking: ranking,
  };
  try {
    const res = await fetch(process.env.NEXT_PUBLIC_RANKING_API_URL, {
      method: "PUT",
      body: JSON.stringify(args),
      headers: {
        "content-type": "application/json",
      },
    });
    const data = await res.json();
  } catch (error) {
    console.log(error);
  }
};

错误信息

ReferenceError: TextEncoder is not defined

      20 |   try {
      21 |     const res = await fetch(process.env.NEXT_PUBLIC_RANKING_API_URL, {
    > 22 |       method: "POST",
         |                                                    ^
      23 |       headers: {
      24 |         "content-type": "application/json",
      25 |       },

解决方法

这个错误源于TextEncoder属于Node.js的util模块,在jsdom测试环境中未被全局暴露,而你的apiCalls.js引入了mongoose,其内部会用到TextEncoder。

方案1:全局注入TextEncoder/TextDecoder

修改jest.config.js,添加setupFilesAfterEnv配置项:

const nextJest = require("next/jest");

const createJestConfig = nextJest({
  dir: "./",
});

const config = {
  coverageProvider: "v8",
  testEnvironment: "jsdom",
  moduleNameMapper: {
    "^@/components/(.*)$": "<rootDir>/components/$1",
    "^@/lib/(.*)$": "<rootDir>/lib/$1",
  },
  setupFilesAfterEnv: ["<rootDir>/jest.setup.js"],
};

module.exports = createJestConfig(config);

在项目根目录新建jest.setup.js,写入以下内容:

import { TextEncoder, TextDecoder } from 'util';

global.TextEncoder = TextEncoder;
global.TextDecoder = TextDecoder;

方案2:切换测试环境为Node.js

如果API测试不需要浏览器DOM相关API,可直接将测试环境改为node:

const config = {
  coverageProvider: "v8",
  testEnvironment: "node",
  moduleNameMapper: {
    "^@/components/(.*)$": "<rootDir>/components/$1",
    "^@/lib/(.*)$": "<rootDir>/lib/$1",
  },
};

方案3:升级依赖版本

检查并升级jest、next/jest和jsdom到最新版本,部分旧版本的jsdom存在TextEncoder全局注入的问题。


内容的提问来源于stack exchange,提问作者Martín Rodas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 07:57:02