如何在 webpack 中使用 jest?

Posted

技术标签:

【中文标题】如何在 webpack 中使用 jest?【英文标题】:How to use jest with webpack? 【发布时间】:2015-05-06 09:01:57 【问题描述】:

我使用webpack 来开发一个 React 组件。这是它的一个简单版本:

'use strict';

require('./MyComponent.less');

var React = require('react');

var MyComponent = React.createClass(
  render() 
    return (
      <div className="my-component">
        Hello World
      </div>
    );
  
);

module.exports = MyComponent;

现在,我想使用jest 测试这个组件。这是我的package.json 的相关内容:

"scripts": 
  "test": "jest"
,
"jest": 
  "rootDir": ".",
  "testDirectoryName": "tests",
  "scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
  "unmockedModulePathPatterns": [
    "react"
  ]

运行npm test时,出现以下错误:

语法错误:/Users/mishamoroshko/react-component/src/tests/MyComponent.js:/Users/mishamoroshko/react-component/src/MyComponent.js:/Users/mishamoroshko/react-component/src/MyComponent。 less:意外的令牌非法

看起来 webpack 需要在 jest 运行测试之前处理 require('./MyComponent.less')

我想知道我是否需要使用jest-webpack 之类的东西。如果是,有没有办法指定多个scriptPreprocessors? (注意我已经用babel-jest了)

【问题讨论】:

您是否尝试过使用 webpack 将测试文件编译到单独的目录中,然后在生成的文件上运行 jest? 我也遇到过这个问题,并且有一个类似的问题here 概述了我尝试过的方法(这里也提到了)以及为什么它们还不够。 【参考方案1】:

我发现忽略所需模块的最干净的解决方案是使用moduleNameMapper config(适用于最新版本 0.9.2)

文档很难理解。我希望以下内容会有所帮助。

将 moduleNameMapper 键添加到您的 packages.json 配置中。项目的键应该是所需字符串的正则表达式。带有“.less”文件的示例:

"moduleNameMapper":  "^.*[.](less|LESS)$": "EmptyModule" ,

将 EmptyModule.js 添加到您的根文件夹:

/**
 * @providesModule EmptyModule
 */
module.exports = '';

注释很重要,因为 moduleNameMapper 使用 EmptyModule 作为该模块的别名 (read more about providesModule)。

现在每个与正则表达式匹配的 require 引用都将被替换为空字符串。

如果您将 moduleFileExtensions 配置与“js”文件一起使用,请确保您还将 EmptyModule 添加到“unmockedModulePathPatterns”中。

这是我最终得到的笑话配置:

"jest": 
  "scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
  "moduleFileExtensions": ["js", "json","jsx" ],
  "moduleNameMapper": 
    "^.*[.](jpg|JPG|gif|GIF|png|PNG|less|LESS|css|CSS)$": "EmptyModule"
  ,
  "preprocessorIgnorePatterns": [ "/node_modules/" ],
  "unmockedModulePathPatterns": [
    "<rootDir>/node_modules/react",
    "<rootDir>/node_modules/react-dom",
    "<rootDir>/node_modules/react-addons-test-utils",
    "<rootDir>/EmptyModule.js"
  ]

【讨论】:

确认此方法有效,只需确保您在评论之前的EmptyModule.js 中没有任何内容(如另一条评论) 这也是Facebook的create-react-app使用的方案。修改了一点点,但它是相同的技术。如果您在新的create-react-app 项目上npm run eject,您将能够非常清楚地看到实现。 这行得通,但是……它真的是最干净的解决方案吗?如果我们可以告诉 Jest 忽略某些文件而不必创建一个空模块,那就太好了 这似乎对我没有任何影响。仍然得到相同的unexpected token . 错误。【参考方案2】:

我最终得到了以下 hack:

// package.json

"jest": 
  "scriptPreprocessor": "<rootDir>/jest-script-preprocessor",
  ...



// jest-script-preprocessor.js
var babelJest = require("babel-jest");

module.exports = 
  process: function(src, filename) 
    return babelJest.process(src, filename)
      .replace(/^require.*\.less.*;$/gm, '');
  
;

但是,我仍然想知道这个问题的正确解决方案是什么。

【讨论】:

我还没有找到更好的解决方案?除非有人知道补丁。我觉得这样做很恶心:( - 唉,谢谢你的工作。 modulePathIgnorePatterns 不会在没有代码的情况下达到同样的效果吗?还有modulePaths,我可以将它与 webpack 别名一起使用。它还需要一个 simlink 来匹配别名。讨厌的黑客... 不知道为什么它对我不起作用,仍然看到同样的错误:( jest.moduleNameMapper 与模拟文件是要走的路,记录在here。 (除非你被旧版本的 Jest 卡住了【参考方案3】:

我刚刚发现使用 Jest 的 moduleNameMapper 配置更简单。

// package.json

"jest": 
    "moduleNameMapper": 
      "^.+\\.scss$": "<rootDir>/scripts/mocks/style-mock.js"
    


// style-mock.js

module.exports = ;

更多详情请访问 Jest 的tutorial page。

【讨论】:

现在这是最好的方法,今天你链接到的 Jest 文档非常清楚地解释了你需要做什么。尽管我的设置甚至包括 TypeScript,但我已经设法用它很快地进行了设置。【参考方案4】:

我最近发布了Jestpack,这可能会有所帮助。它首先使用 Webpack 构建您的测试文件,因此任何自定义模块解析/加载器/插件等都可以正常工作,您最终会使用 javascript。然后它为 Jest 提供了一个自定义模块加载器,它了解 Webpack 模块运行时。

【讨论】:

【参考方案5】:

来自Jest docs:

// in terminal, add new dependency: identity-obj-proxy
npm install --save-dev identity-obj-proxy

// package.json (for CSS Modules)

  "jest": 
    "moduleNameMapper": 
      "\\.(css|less)$": "identity-obj-proxy"
    
  

上面的 sn-p 会将所有.less 文件路由到新的依赖项identity-obj-proxy,它会在调用时返回一个带有类名的字符串,例如'styleName'styles.styleName

【讨论】:

【参考方案6】:

我认为一个不那么 hacky 的解决方案是将你的预处理器包装在一个与 javascript 文件匹配的文件名的条件中:

if (filename.match(/\.jsx?$/)) 
    return babelJest.process(src, filename);
 else 
    return '';

即使您没有在要求行中明确设置扩展名并且不需要在源代码上进行正则表达式替换,这也有效。

【讨论】:

对此要小心——我有一个类似的预处理器,最后得到了TypeError: React.createClass is not a function,因为return ''; 语句意味着您“丢弃”诸如包含node_modules.js 文件之类的东西。 这可能是我的package.json 的问题 - 可能有一种方法可以忽略这些文件,但我认为将上述行重写为:if (filename.match(/\.jsx?$/)) return babelJest.process(src, filename); else return src; //change here 【参考方案7】:

这种模式我也遇到过类似的问题

import React,  PropTypes, Component  from 'react';
import styles from './ContactPage.css';
import withStyles from '../../decorators/withStyles';

@withStyles(styles)
class ContactPage extends Component 

参见https://github.com/kriasoft/react-starter-kit/blob/9204f2661ebee15dcb0b2feed4ae1d2137a8d213/src/components/ContactPage/ContactPage.js#L4-L7的示例

运行 Jest 我有两个问题:

.css 的导入 申请装饰器@withStyles (TypeError: &lt;...&gt; (0 , _appDecoratorsWithStyles2.default)(...) is not a function)

第一个已通过在脚本预处理器中模拟 .css 本身来解决。

第二个已通过使用unmockedModulePathPatterns从自动模拟中排除装饰器来解决

module.exports = 
  process: function (src, filename) 

    ...

    if (filename.match(/\.css$/)) src = '';

    ...

    babel.transform(src, ...
  

基于https://github.com/babel/babel-jest/blob/77a24a71ae2291af64f51a237b2a9146fa38b136/index.js的示例

另请注意:当您使用 jest 预处理器时,您应该清理缓存:

$ rm node_modules/jest-cli/.haste_cache -r

【讨论】:

rm node_modules/jest-cli/.haste_cache -r 这救了我的命。谢谢。【参考方案8】:

从 Misha 的回复中获得灵感,我创建了一个 NPM 包来解决这个问题,同时还处理了我遇到的更多场景:

webpack-babel-jest

希望这可以为下一个人节省几个小时。

【讨论】:

【参考方案9】:

如果您使用 babel,您可以在 babel 转换期间使用 https://github.com/Shyp/babel-plugin-import-noop 之类的东西去除不需要的导入,并配置您的 .babelrc test env 以使用该插件,如下所示:


  "env": 
    "development": 
      ...
    ,
    "test": 
      "presets": [ ... ],
      "plugins": [
        ["import-noop", 
          "extensions": ["scss", "css"]
        ]
      ]
    
  

【讨论】:

【参考方案10】:

我们在 CSS 文件中遇到了类似的问题。正如您之前提到的jest-webpack 很好地解决了这个问题。您也不必模拟或使用任何模块映射器。对我们来说,我们将 npm 测试命令从 jest 替换为 jest-webpack,它就可以正常工作了。

【讨论】:

【参考方案11】:

问这个问题已经 5 年了,但对我来说仍然很困惑,我不敢相信让 jest 和 webpack 工作这么难,直到我尝试了这个:

添加 babel-jest 和 babel 预设:

yarn add --dev babel-jest @babel/preset-env

添加 babel 配置:

// babel.config.js
module.exports = 
  presets: ["@babel/preset-env"]
;

这可能对你有用,也可能对你不起作用,因为问题标题仅指 jest 和 webpack,人们可能会像我一样偶然发现这篇文章,对如何使 jest 与 webpack 一起工作一无所知,我希望至少我的回答有所帮助如果这对您没有帮助,请联系他们。

【讨论】:

【参考方案12】:

Webpack 是一个很棒的工具,但我不需要用我的 Jest 单元测试来测试它的行为,并且在运行单元测试之前添加一个 webpack 构建只会减慢这个过程。 教科书上的答案是使用 "moduleNameMapper" 选项模拟非代码依赖项

https://facebook.github.io/jest/docs/webpack.html#handling-static-assets

【讨论】:

对不起,我错过了@tien-do 的答案并被骗了。

以上是关于如何在 webpack 中使用 jest?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Jest 测试中使用我的 webpack 的 html-loader 导入?

如何在Webpack中使用Jest和ES6功能

在 Jest 测试中使用 Webpack 的 DefinePlugin 变量

如何同时观看运行 Jest 测试的 webpack-dev-server?

Jest 在没有 webpack 的情况下配置 typescript

如何将外部内容从 webpack 传递给 jest?