用Jest运行ES module的测试

2023-02-09大约12分钟

在为JS代码写单元测试的时候,我们不可避免地需要为ES模块的代码编写测试。如果你遇到类似下面的错误,那么你就很可能需要处理一下模块转换的问题了。

    Test suite failed to run

    Jest encountered an unexpected token

    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

    By default "node_modules" folder is ignored by transformers.

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     • If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

    SyntaxError: Cannot use import statement outside a module

以上的错误信息里,包含了常见问题的解决办法。不过由于写得并不是特别详细,因此真正遇到这个问题的时候,还是需要多花点时间来研究一下。

直接运行ES代码

不用经过Babel的转换,直接运行ES代码感觉是很爽的事情。ES代码如果能直接运行,何必经过转换到CJS模块再运行呢? 最新的Node.js V18已经支持ES模块,只要在package.json里设置"type": "module"即可。 但是呢,截至目前,Jest对ECMAScript Modules (ESM)的支持仍然是试验性的,因此,我们需要做些变动。

实际上也很简单,就是运行测试的时候,为node加上这个参数--experimental-vm-modules,比如:

node --experimental-vm-modules node_modules/jest/bin/jest.js
NODE_OPTIONS=--experimental-vm-modules npx jest

在package.json的scripts里的test命令:

"scripts": {
    "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
}

如果你用VSCode,用一些运行Jest测试的插件,那么,也需要为这些插件做一下配置。这里拿JestRunner来举例。

首先打开JestRunner的设置: JestRunner的设置

上图中,有两个地方要设置:

Debug的参数

点击在settings.json中编辑,打开settings.json后,添加:

"jestrunner.debugOptions": {
    "runtimeArgs": ["--experimental-vm-modules"]
}

debugOptions里的配置,实际上指的是VSCode的debug的配置:

https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_launch-configuration-attributes

需要更多参数的时候,可以参考VSCode的文档。

Run的参数

JestRunner: Jest Command里填上:

node --experimental-vm-modules node_modules/jest/bin/jest.js

这样,Jest就可以运行ES模块的测试了。

使用Babel转换

安装依赖

安装@babel/plugin-transform-modules-commonjs包:

npm i -D @babel/plugin-transform-modules-commonjs

创建.babelrc文件

这里的配置仅仅当NODE_ENV=test的时候,才有效:

{
  "env": {
    "test": {
      "plugins": ["@babel/plugin-transform-modules-commonjs"]
    }
  }
}

这样,就可以运行你的测试代码了,无论你的测试代码是ES还是CJS的模块都可以。

Babel的这个改动,仅当所依赖的包都是CJS模块的时候才可以正常运行

如果依赖的NPM包有ES模块,由于Jest默认情况下,会忽略node_modules下的模块:

By default "node_modules" folder is ignored by transformers.

因此,如果代码里依赖的node_modules下的包是用ES模块实现的话,那么任然会碰到这个错:

SyntaxError: Cannot use import statement outside a module

这个时候,需要依赖这个指引:

To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config. 来解决问题。

可以参考:https://juejin.cn/post/6898738304754286605