🅰️

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

2021/07/04に公開

(190829 追記)

久々に試してみたらjest-preset-angular v7.1.1以降にて解決を感じました。このバージョン以降ならばjest@24.xと組み合わせても大丈夫そうです。


こんにちは、 @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の箇所が増えています。

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で失敗するを報告しました。

Discussion