Vitest解析含Redux的文件导入失败,测试无关函数也报错
问题
在Next.js(TypeScript)项目中结合Redux与Vitest使用时,遇到以下异常:
- Redux切片运行正常,Vitest测试不涉及Redux钩子的文件函数时工作正常。
- 但测试包含Redux代码的文件中的无关函数时,Vitest抛出
Failed to resolve import.....Does the file exist?错误,即使被测试函数与Redux完全无关。 - 注释组件内的
dispatch(someAction())代码后,测试立即通过,推测是Vitest配置未适配Next.js + Redux的模块解析与依赖处理逻辑。
问题代码示例
// DummyComponent.tsx import { useAppDispatch } from 'store/hooks' import { someAction } from 'store/some-slice/action' export default function DummyComponent() { const dispatch = useAppDispatch() const dummyAction = () => { dispatch(someAction()) } return <button onClick={dummyAction}>Run action</button>; } // ↓↓↓ 待测试的无关函数 ↓↓↓ function hasNothingToDoWithRedux(a: number, b: number) { return a + b; } if (import.meta.vitest) { const { describe, expect, it } = import.meta.vitest; describe("Testing hasNothingToDoWithRedux", () => { it("Sums two values correctly", () => { const a = 2; const b = 1; expect(hasNothingToDoWithRedux(a, b)).toBe(3); }); }); }
相关配置文件
vite.config.ts
import { defineConfig } from "vitest/config"; export default defineConfig({ define: { "import.meta.vitest": "undefined", }, test: { includeSource: ["./**/*.{js,ts,tsx}"], }, });
tsconfig.json
{ "compilerOptions": { "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "incremental": true, "baseUrl": ".", "types": ["vitest/importMeta"] }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], "exclude": ["node_modules"] }
package.json
{ "name": "shift-management", "version": "0.1.0", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint", "test": "vitest" }, "dependencies": { "@reduxjs/toolkit": "^1.8.6", "luxon": "^3.0.4", "next": "13.0.2", "react": "18.2.0", "react-dom": "18.2.0", "react-icons": "^4.6.0", "react-redux": "^8.0.4", "react-transition-group": "^4.4.5", "sass": "^1.55.0" }, "devDependencies": { "@types/luxon": "^3.0.2", "@types/node": "18.11.9", "@types/react": "18.0.25", "@types/react-dom": "18.0.8", "@types/react-redux": "^7.1.24", "@types/react-transition-group": "^4.4.5", "@vitest/coverage-c8": "^0.25.3", "eslint": "8.25.0", "eslint-config-next": "12.3.1", "typescript": "4.8.4", "vite": "^3.2.4", "vitest": "^0.25.3" } }
解决方案
1. 修复Vitest模块解析路径
Vitest默认模块解析逻辑未适配Next.js的baseUrl配置,导致无法识别store/开头的导入路径。修改vite.config.ts,添加路径别名映射:
import { defineConfig } from "vitest/config"; import path from "path"; export default defineConfig({ define: { "import.meta.vitest": "undefined", }, resolve: { alias: { "store": path.resolve(__dirname, "./store"), }, }, test: { includeSource: ["./**/*.{js,ts,tsx}"], environment: "jsdom", // 适配React组件的测试环境 }, });
2. Mock Redux相关依赖(可选,增强稳定性)
即使路径解析正确,Vitest在处理组件内的Redux钩子时可能因无Provider环境报错。通过mock隔离Redux依赖:
局部mock(内联测试场景)
在DummyComponent.tsx的测试代码中添加mock:
if (import.meta.vitest) { const { describe, expect, it, vi } = import.meta.vitest; // Mock Redux钩子与action vi.mock('store/hooks', () => ({ useAppDispatch: () => vi.fn(), })); vi.mock('store/some-slice/action', () => ({ someAction: vi.fn(), })); describe("Testing hasNothingToDoWithRedux", () => { it("Sums two values correctly", () => { const a = 2; const b = 1; expect(hasNothingToDoWithRedux(a, b)).toBe(3); }); }); }
全局mock(多测试文件场景)
在项目根目录创建vitest.setup.ts:
import { vi } from 'vitest'; vi.mock('store/hooks', () => ({ useAppDispatch: vi.fn(), useAppSelector: vi.fn(), }));
然后在vite.config.ts中配置全局setup文件:
test: { includeSource: ["./**/*.{js,ts,tsx}"], environment: "jsdom", setupFiles: ["./vitest.setup.ts"], },
3. 优化TypeScript配置(可选)
为了让TypeScript更好地识别路径别名,在tsconfig.json中添加paths配置:
"compilerOptions": { // ...其他配置 "baseUrl": ".", "paths": { "store/*": ["./store/*"] }, "types": ["vitest/importMeta"] }
4. 独立测试文件优化(非内联测试场景)
如果使用独立测试文件,可只导入目标函数,避免加载整个组件:
// DummyComponent.test.tsx import { expect, describe, it } from 'vitest'; import { hasNothingToDoWithRedux } from '../components/DummyComponent'; describe("Testing hasNothingToDoWithRedux", () => { it("Sums two values correctly", () => { const a = 2; const b = 1; expect(hasNothingToDoWithRedux(a, b)).toBe(3); }); });
注意:需将hasNothingToDoWithRedux改为导出函数(export function hasNothingToDoWithRedux(...))。
验证
修改完成后,执行yarn vitest,测试应无需注释dispatch代码即可正常通过。
内容的提问来源于stack exchange,提问作者Kenji Emura
相关产品推荐
相关产品推荐

