Angular

Angular v8とjest-preset-angularの相性が悪いので解決策をまとめた

こんにちは、 @okunokentaro です。タイトルでなんの事を指しているのか分かる人向けに書いているので、 jest-preset-angularの詳細解説は割愛します。

Angular v8 rc段階にて判明した問題と、そのworkaroundをまとめています。将来的にjest-preset-angularjest自体のアップデートが入れば、この記事が不要になる可能性は大いにあります。


2019/5/15時点で何が起きているか

Angular v7まででjestを併用したければjest-preset-angularを使うのが常でした。jest-preset-angular@7.0.xjest@24系に乗ったことで、transform, stringifyContentPathRegexなどの各種オプションの解釈が変わっている認識です。しかし、このままだとAngularテンプレートhtml、スタイルscss等の読み込みにて、jest実行時にエラーを起こします。

Error: connect ECONNREFUSED 127.0.0.1:80

at Object.dispatchError (/path/to/node_modules/jest-environment-jsdom-thirteen/node_modules/jsdom/lib/jsdom/living/xhr-utils.js:60:19)
at Request.client.on.err (/path/to/node_modules/jest-environment-jsdom-thirteen/node_modules/jsdom/lib/jsdom/living/xmlhttprequest.js:674:20)
at Request.emit (events.js:194:15)
at Request.onRequestError (/path/to/node_modules/request/request.js:881:8)
at ClientRequest.emit (events.js:189:13)
at Socket.socketErrorListener (_http_client.js:392:9)
at Socket.emit (events.js:189:13)
at emitErrorNT (internal/streams/destroy.js:82:8)
at emitErrorAndCloseNT (internal/streams/destroy.js:50:3)
at process._tickCallback (internal/process/next_tick.js:63:19) undefined

この問題の解決法は根気よくたどればあるのでしょうが、現状はjest-preset-angular@6.0.2を使うというworkaroundに落ち着いている方が多いと思います。


jest-preset-angular@6.0.2の寿命が来た

Angular v8になるとこのworkaroundに問題が起きます。

Angular CLI v8では新規プロジェクト作成時にcore-jsがインストールされないようになりました。実際、v7初期まではインストールされていたcore-jsは、Angular CLI v7.x以降省かれるようになっています。モダンブラウザの多くがpolyfillを必要しなくなったため容量の削減が目的と推測されます。

jest-preset-angularは、特にv6.0.2に関してはこのpolyfillを必須としてpeerDependenciesに組み入れています。そのため、Angular CLI v8にて作成したプロジェクトにてjest-preset-angular@6.0.2を併用すると、module不足としてテストの実行ができません。

さらなるworkaroundとしてcore-jsをインストールすることも考えられますが、将来的なことを考えると、jest-preset-angular@7.0.xで起こっているテンプレートの読み込みに失敗する問題を解決させたほうがよさそうです。

jest-preset-angular@7.0.xにて起こっている問題の解決法は現在分かっておりません。下記参照。


問題の本質

切り分けの結果、jest-preset-angular@7.0.x自体が悪なのではなく、jest@23.xjest@24.xになったことで問題になっているというところまでは掴めました。これはjest@24.xが依存しているjsdomscssを読めないことに起因しています。その証拠に上記エラーはObject.dispatchError (jsdom/lib/jsdom/living/xhr-utils.js:60:19)が発していますし、scssをやめてcssに変更したらjest-preset-angular@7.0.x上でオールグリーンとなりました。


解決法

これもworkaroundの一種ですが、比較的実行しやすい手段を見つけたのでお知らせします。

npm i -D jest@24.8.0 jest-preset-angular@7.1.0 identity-obj-proxy

インストール物は以上です。バージョンは執筆日2019/5/15時点のものなので、閲覧日と離れてる場合はアテにしてはいけません。

インストールしたらjest-preset-angularのために用意しているであろうjest.config.jsに書き加えます。moduleNameMapper, identity-obj-proxyの箇所が増えています。


jest.config.js

module.exports = {

preset: 'jest-preset-angular',
setupFilesAfterEnv: ['<rootDir>/src/setup-jest.ts'],
testPathIgnorePatterns: ['<rootDir>/src/test.ts'],
transform: {
'^.+\\.(ts|js|html)$': 'ts-jest',
},
moduleNameMapper: {
'\\.(css|scss)$': 'identity-obj-proxy',
},
globals: {
'ts-jest': {
tsConfig: 'tsconfig.test.json',
stringifyContentPathRegex: '\\.html$',
},
},
};

identity-obj-proxyはファイルの依存解決時に指定の拡張子をなにがしかのオブジェクトとして扱ってくれるライブラリのようです。深追いはしてないです。(本家Qiita記事をざっと眺めた理解)

これによって、scssのファイル解決が失敗しconnect ECONNREFUSEDとなる問題を解決できます。

なお、このworkaroundはNrwl/NxのJestプラグイン実装より着想を得ました。(thx! 情報提供 @lacolaco )

そこまで筋の悪い解決法ではない気がするので、しばらく様子見つつAngular v8, Jest v24, jest-preset-angular構成で進めたい方はこの方法を試してみるとよいと思います。jest-preset-angular側にはcore-jsが不要であるためpeerから除去してほしいと、identity-obj-proxyがないとscssで失敗するを報告しました。