Next.js配置Jest运行测试时出现import语法报错问题
近几日排查问题时遇到一类常见报错,对应错误信息如下:
Jest encountered an unexpected token /__tests__/index.test.js:16 import React from "react"; ^^^^^^ SyntaxError: Cannot use import statement outside a module at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1796:14)
报错定位到的import React from "react";语句疑似Next.js自动追加到index.test.js文件末尾,直接触发了「无法在模块外使用import语句」的语法错误。已查阅相关技术帖子和Next.js、Jest官方文档,当前项目配置如下。
package.json
{ "name": "some-app", "version": "0.1.0", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint", "test": "jest --watch" }, "dependencies": { "next": "12.1.6", "react": "18.1.0", "react-dom": "18.1.0" }, "devDependencies": { "@babel/plugin-syntax-jsx": "^7.17.12", "@babel/preset-env": "^7.18.2", "@babel/preset-react": "^7.17.12", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.3.0", "@types/jest": "^28.1.3", "@types/node-fetch": "^2.6.2", "babel-jest": "^28.1.1", "eslint": "8.16.0", "eslint-config-next": "12.1.6", "identity-obj-proxy": "^3.0.0", "jest": "^28.1.1", "jest-environment-jsdom": "^28.1.1", "node-fetch": "^3.2.6", "ts-jest": "^28.0.5", "typescript": "^4.7.4" }, "description": "This is a Next.js project bootstrapped with create-next-app.", "main": "jest.config.js", "repository": { "type": "git" }, "author": "", "license": "ISC", "bugs": {} }
测试文件 /__tests__/index.test.js
import { render, screen } from '@testing-library/react' import Home from '../pages/index' import '@testing-library/jest-dom' import React from "react"; describe('Home', () => { it('renders a h', () => { render(<Home />) const link = screen.getByRole('link', { name: "Library", }) expect(link).toHaveAttribute('href', '/storage-area') }) });
jest.config.js
module.exports = { collectCoverage: true, // node 14.x版本下v8覆盖率提供工具备较好速度与可用的报告效果 coverageProvider: 'v8', collectCoverageFrom: [ '**/*.{js,jsx,ts,tsx}', '!**/*.d.ts', '!**/node_modules/**', '!<rootDir>/out/**', '!<rootDir>/.next/**', '!<rootDir>/*.config.js', '!<rootDir>/coverage/**', ], moduleNameMapper: { // 处理CSS Modules导入 '^.+\.module\.(css|sass|scss)$': 'identity-obj-proxy', // 处理普通CSS导入 '^.+\.(css|sass|scss)$': '<rootDir>/__mocks__/styleMock.js', // 处理图片等静态资源导入 '^.+\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i': `<rootDir>/__mocks__/fileMock.js`, // 处理路径别名 '^@/components/(.*)$': '<rootDir>/components/$1', }, // 测试运行前的初始化配置 setupFilesAfterEnv: ['<rootDir>/jest.setup.js'], testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/.next/'], testEnvironment: 'jsdom', transform: { // 使用babel-jest配合预设转译测试文件 '^.+\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel', '@babel/preset-react',"@babel/preset-env" ]}], }, transformIgnorePatterns: [ '/node_modules/', '^.+\.module\.(css|sass|scss)$', ], }
按优先级依次尝试以下方案:
修正babel配置冲突
手动配置@babel/preset-env和@babel/preset-react会和Next.js内置的next/babel预设冲突,导致转译失效。把jest.config.js里transform项的preset配置改成仅保留next/babel即可,修改后配置为:transform: { '^.+\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }], },Next.js的babel预设已经内置了react、env相关转译规则,不需要额外叠加其他预设。
调整测试文件存放位置
不要把测试文件放在根目录的__tests__文件夹下,Next.js 12.x会默认对根目录下的文件做特殊处理,自动注入React import语句。把测试文件移到对应页面/组件的同级目录,命名为index.test.js和页面文件同存,或者新建src/__tests__目录存放测试用例,避开Next.js的根目录文件扫描规则。修正transformIgnorePatterns配置
当前配置会跳过所有node_modules下的文件转译,部分ESM格式的依赖(比如安装的node-fetch v3是纯ESM包)不会被babel转译,会触发同类import报错。把配置修改为:transformIgnorePatterns: [ '/node_modules/(?!(next|react|react-dom|@testing-library)/)', '^.+\.module\.(css|sass|scss)$', ],强制babel转译node_modules下Next.js、React、testing-library相关的ESM格式代码。
清理缓存重试
执行以下命令清除Jest和Next.js的缓存后重新运行测试:rm -rf node_modules/.cache npx jest --clearCache旧的转译缓存经常会导致配置修改后不生效的问题。
内容的提问来源于stack exchange,提问作者Byron

