Jest 默认无法解析 CSS 文件,需通过 moduleNameMapper 忽略或模拟导入;推荐用 identity-obj-proxy 或自定义 __mocks__/styleMock.js 统一处理,避免动态 mock 导致测试污染。

Jest 默认无法解析 CSS 文件,直接 import 或 require 会报错;必须通过配置让 Jest 忽略或模拟这些导入。
为什么 import './style.css' 在 Jest 中会报错
Node.js 和 Jest 运行时默认只处理 JavaScript/TypeScript 文件。遇到 import './Button.css' 这类语句时,Jest 尝试加载并执行该文件,但 CSS 不是合法的 JS 模块,于是抛出类似 Cannot find module './Button.css' 或 Unexpected token 的错误。
- 这个错误不是代码写错了,而是模块解析链在 Jest 环境中断了
- Webpack/Vite 等构建工具能处理 CSS 是因为它们有专门的 loader;Jest 没有,也不该有
- 单元测试关注逻辑,不关心样式是否生效,所以“忽略 CSS 导入”是合理且标准做法
用 moduleNameMapper 配置 CSS 模块别名
这是最常用、最稳定的方式:告诉 Jest “所有匹配 \.(css|less|scss|sass|styl)$ 的路径,都映射到一个空模块”。
在 jest.config.js 中添加:
立即学习“前端免费学习笔记(深入)”;
module.exports = {
moduleNameMapper: {
'\.(css|less|scss|sass|styl)$': 'identity-obj-proxy'
}
};-
identity-obj-proxy是社区广泛使用的轻量包,它导出一个空对象,支持属性访问(比如import styles from './Button.css'; styles.primary不会报错) - 如果项目里压根不用 CSS Modules(即没写
import styles from './x.css'),也可以用更简单的'<rootdir>/__mocks__/styleMock.js'</rootdir>,内容就一行:module.exports = {}; - 注意正则要加转义,
\.表示字面量的点号,否则会误匹配任意字符
手动 mock __mocks__/styleMock.js 更可控
当项目中有少量 CSS Modules 且需要模拟特定类名行为时,自定义 mock 文件比 identity-obj-proxy 更清晰。
创建 __mocks__/styleMock.js(放在项目根目录,与 src 同级):
module.exports = {
button: 'Button_button__abc123',
'btn-primary': 'Button_btn-primary__def456'
};再在 jest.config.js 中指向它:
moduleNameMapper: {
'\.(css|scss)$': '<rootDir>/__mocks__/styleMock.js'
}- 这样所有
import styles from './x.scss'都会得到这个固定对象,断言styles.button是否被使用就变得可测 - 避免依赖第三方包,减少 CI 构建不确定性
- 注意:这个文件必须是 CommonJS 格式(
module.exports),ESM 的export default在 Jest 的 mock 解析中可能不生效
ESM 项目下 import * as css from './x.css' 的陷阱
如果你在 ESM 模式("type": "module")下写了 import * as css from './x.css',Jest 的 moduleNameMapper 可能不生效 —— 因为 ESM 的静态导入在解析阶段就锁定了模块类型。
- 此时必须配合
transformIgnorePatterns确保 CSS 文件不被 Babel 处理,同时确保moduleNameMapper规则优先级足够高 - 更稳妥的做法是:在测试入口(如
setupTests.js)中提前注册 mock:jest.mock('./x.css', () => ({}));,但仅限具体路径,不能通配 - 真实项目中应尽量避免 ESM 下直接 import CSS;构建时由打包器处理,测试时就该彻底剥离
真正容易被忽略的不是“怎么配”,而是“谁来负责清理”:CSS mock 是全局生效的,一旦在某个测试文件里用 jest.mock('./x.css', ...) 覆盖了默认行为,其他测试可能意外拿到这个 mock。所以除非必要,坚持用 moduleNameMapper 统一处理,别在测试块内动态 mock 样式文件。


















