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
相关产品推荐
相关产品推荐

