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 需要require('./MyComponent.less')在 jest 运行测试之前进行处理。

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

4

12 回答 12

25

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

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

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

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

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

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

注释很重要,因为 moduleNameMapper 使用 EmptyModule 作为该模块的别名(阅读有关 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"
  ]
}
于 2016-03-21T17:22:29.503 回答
19

我最终得到了以下黑客:

// 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, '');
  }
};

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

于 2015-03-05T09:22:32.143 回答
10

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

// package.json

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

// style-mock.js

module.exports = {};

更多细节在 Jest 的教程页面

于 2016-11-08T02:56:12.800 回答
4

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

于 2015-10-18T20:00:07.053 回答
3

来自Jest 文档

// 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"
    }
  }
}

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

于 2017-08-29T21:19:43.507 回答
2

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

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

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

于 2015-10-17T19:27:07.530 回答
1

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

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: <...> (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
于 2015-11-08T00:04:00.507 回答
0

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

webpack-babel-jest

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

于 2015-07-10T21:24:46.270 回答
0

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

{
  "env": {
    "development": {
      ...
    },
    "test": {
      "presets": [ ... ],
      "plugins": [
        ["import-noop", {
          "extensions": ["scss", "css"]
        }]
      ]
    }
  }
}
于 2016-11-10T19:09:58.527 回答
0

It has been 5 years since this question asked, yet its still very confusing for me, I can't believe that its so hard to make jest and webpack works, until I tried this:

Add babel-jest and babel preset:

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

Add babel config:

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

This may or may not work for you, since the question title only refers to jest and webpack, people may stumble upon this article just like me that's clueless how to make jest works with webpack, and I hope at least my answer helped them if this doesn't help you.

于 2020-07-30T05:13:32.867 回答
0

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

于 2018-02-14T18:02:24.200 回答
-1

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

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

于 2017-04-27T18:39:15.117 回答