使用Jest模拟Next.js的appSettings模块时出现初始化前访问错误
解决Jest模拟Next.js appSettings模块时的ReferenceError问题
问题原因
你遇到的「ReferenceError: Cannot access '_appsettings' before initialization」错误,核心原因是Jest会自动把jest.mock调用提升到所有代码(包括import语句)的最顶部执行。你原本通过import导入的appSettings变量,在jest.mock的工厂函数执行时还未完成初始化,导致提前访问触发报错。
可行解决方案
方案1:在mock工厂函数中用require引入mock文件
把mock文件的引入放到jest.mock的工厂函数内部,利用require不会被Jest提升的特性,避免初始化顺序问题:
import "@testing-library/jest-dom"; import { render, screen } from "@testing-library/react"; import { DonorSelector } from "../src/views/DonorSelector"; // 在mock工厂函数内用require加载mock对象 jest.mock("../src/helpers/app-settings.helper", () => ({ ...require("../mocks/app-settings").appSettings, })); // 测试中需要用到mock值时,再正常导入 import { appSettings } from "../mocks/app-settings"; describe("DonorSelector", () => { it("should receive appSettings with mock values", () => { render(<DonorSelector content={content} setLanguageCallback={() => {}} />); expect(screen.getByTestId("app-settings")).toHaveTextContent( JSON.stringify(appSettings) ); }); });
方案2:直接在mock工厂函数中定义mock对象
如果mock内容不复杂,可以直接把配置写在jest.mock的工厂函数里,省去单独导入的步骤:
import "@testing-library/jest-dom"; import { render, screen } from "@testing-library/react"; import { DonorSelector } from "../src/views/DonorSelector"; // 直接在mock中定义appSettings的内容 jest.mock("../src/helpers/app-settings.helper", () => ({ appSettings: { apiBaseUrl: "https://mock.example.com", defaultLocale: "zh-CN", // 其他你的配置项 }, })); // 从被mock的模块中导入,确保测试用的是同一个mock值 import { appSettings } from "../src/helpers/app-settings.helper"; describe("DonorSelector", () => { it("should receive appSettings with mock values", () => { render(<DonorSelector content={content} setLanguageCallback={() => {}} />); expect(screen.getByTestId("app-settings")).toHaveTextContent( JSON.stringify(appSettings) ); }); });
方案3:使用jest.doMock避免自动提升(不推荐)
如果一定要保留单独的mock文件导入,可以用jest.doMock代替jest.mock,它不会被自动提升,需要确保在组件导入前执行:
import "@testing-library/jest-dom"; import { render, screen } from "@testing-library/react"; import { appSettings } from "../mocks/app-settings"; // 用doMock替代mock,手动控制执行顺序 jest.doMock("../src/helpers/app-settings.helper", () => ({ ...appSettings, })); // 必须在doMock之后再导入组件 const { DonorSelector } = require("../src/views/DonorSelector"); describe("DonorSelector", () => { it("should receive appSettings with mock values", () => { render(<DonorSelector content={content} setLanguageCallback={() => {}} />); expect(screen.getByTestId("app-settings")).toHaveTextContent( JSON.stringify(appSettings) ); }); });
注意事项
- 不要在
jest.mock的工厂函数中直接引用任何被提升的import变量,因为Jest的mock提升机制会让这些变量在此时处于未初始化状态。 - 优先使用方案1或方案2,这两种方式更符合Jest的常规使用习惯,代码可读性也更好。
内容的提问来源于stack exchange,提问作者ComCool
相关产品推荐
相关产品推荐

