環境#
- NX
- PNPM
- lodash-es
- Jest
karma から Jest への移行で以下のエラーが発生しました#
主な原因は、"node_modules" フォルダ内の ESM(ECMAScript Modules)ライブラリが Jest にサポートされていないことです。
Jest の ESM サポートはまだほとんど使用できない実験段階にあるため、現時点では主に会社のプロジェクトで Jest に移行しています。そのため、この問題を解決するために、この記事では transformIgnorePatterns
と moduleNameMapper
の 2 つの設定を使用します。
テストスイートの実行に失敗しました
Jestが予期しないトークンに遭遇しました
Jestはファイルを解析できませんでした。これは、コードまたはその依存関係が非標準のJavaScript構文を使用している場合、またはJestがそのような構文をサポートするように構成されていない場合に発生します。
デフォルトでは、JestはBabelをサポートしており、Babelの設定に基づいてファイルを有効なJSに変換します。
デフォルトでは、トランスフォーマーによって "node_modules"フォルダは無視されます。
以下の操作ができます:
• ECMAScript Modulesを使用しようとしている場合は、https://jestjs.io/docs/ecmascript-modules を参照して有効にする方法を確認してください。
• TypeScriptを使用しようとしている場合は、https://jestjs.io/docs/getting-started#using-typescript を参照してください。
• "node_modules"の一部のファイルを変換するには、設定でカスタムの "transformIgnorePatterns" を指定できます。
• カスタムの変換が必要な場合は、設定で "transform" オプションを指定します。
• 単に非JSモジュール(バイナリアセットなど)をモックしたい場合は、 "moduleNameMapper" の設定オプションでそれらをスタブ化できます。
これらの設定オプションの詳細と例については、ドキュメントを参照してください:
https://jestjs.io/docs/configuration
カスタム変換に関する情報については、次を参照してください:
https://jestjs.io/docs/code-transformation
以下の設定は、lodash-es を参考にしています。
transformIgnorePatterns#
公式ドキュメントの説明は次のとおりです:変換前にすべてのソースファイルパスと一致する正規表現パターンの文字列配列。ファイルパスがいずれかのパターンと一致する場合、変換は行われません。
つまり、transformIgnorePatterns
は、コード変換時に無視するファイルやフォルダを指定するために使用されます。
NX のデフォルトの Jest 設定では、node_modules/(?!.*\\.mjs$)
となっています。
この正規表現は、node_modules/
で始まるフォルダパスに一致し、ただし .mjs
という拡張子を持つフォルダパスは除外します。?!
は否定先読みを表し、このようなフォルダパスには一致しないことを意味します。
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
上記の設定は、拡張子が .mjs
のファイルを ESM から CommonJS に変換して Jest をサポートすることを意味します。
lodash-es の変換の追加#
PNPM もサポートするようにしましょう
const esModules = ['.*\\.mjs$', 'lodash-es'].join('|');
export default {
...
transformIgnorePatterns: [`node_modules/(?!.pnpm|${esModules})`],
...
}
変換後の失敗数は 15 から 11 に減少しましたが、この方法では変換に余分なコストがかかり、51 秒かかります。ただし、最初の変換が完了するとキャッシュされ、変換の必要がなくなるようです。
より少ないコストの方法 moduleNameMapper#
この方法では、ライブラリ自体が対応する CommonJS が必要であり、変換は必要ありません。12 秒で実行できます。
export default {
...
moduleNameMapper: {
'^lodash-es$': 'lodash',
},
...
}
最終的な設定の参考例#
/* eslint-disable */
const esModules = ['.*\\.mjs$'].join('|');
export default {
displayName: 'pc',
preset: '../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../coverage/apps/pc',
moduleNameMapper: {
'^lodash-es$': 'lodash',
},
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
},
],
},
transformIgnorePatterns: [`node_modules/(?!.pnpm|${esModules})`],
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
'jest-preset-angular/build/serializers/html-comment',
],
};