😀

webpack+Jest環境のmswをバージョン2にアップグレードしてハマったこと

2023/12/22に公開

はじめに

大幅な変更が加えられたので、置換する時間(量が多いとなかなかつらい)
うまく動かなかった場合の調査時間が取れない場合はやめておいたほうが無難だと思うので

先にこちらを一読してください

将来的にvite,vitestを使う予定であれば早めに移行したほうがよさそうです

環境

jest: 29.7.0
react: 18.2.0
node: 20.9.0
next: 14.0.3

TL;DR

migrationsに沿って置換を行っていく
undici をv5のものを使う
jest.polyfills.jsの設定を見直す
jest.useFakeTimers({ legacyFakeTimers: true })を設定してみる

移行フロー

1.mockの動作確認

とりあえずフローに沿って置換していきフロントのmockとして使用しているのであればJestの確認より先にブラウザ上の確認を行い、想定通りのレスポンスが返却されているか確認する

2.Jest側の記載を置換

ブラウザ上の動作確認後レスポンスに合わせて置換してく

3.jest.polyfills.jsファイルを追加

Jest環境だとこちらにある通り

Jestが意図的にNode.jsのグローバル汚染していくため jest.polyfills.js を追加し、
手動で今まで通りにする必要があります

手順としては undici をインストールしてファイルを追加するのですが
2023/12/22現在undiciの最新のv6にするとうまく動かなくなるので 5.28.2をインストール

yarn add -D undici@5.28.2

次にガイド通り jest.polyfills.js を追加する

jest.polyfills.js
const { TextDecoder, TextEncoder } = require('node:util')
 
Object.defineProperties(globalThis, {
  TextDecoder: { value: TextDecoder },
  TextEncoder: { value: TextEncoder },
})
 
const { Blob, File } = require('node:buffer')
const { fetch, Headers, FormData, Request, Response } = require('undici')
 
Object.defineProperties(globalThis, {
  fetch: { value: fetch, writable: true },
  Blob: { value: Blob },
  File: { value: File },
  Headers: { value: Headers },
  FormData: { value: FormData },
  Request: { value: Request },
  Response: { value: Response },
})

以前までは whatwg-fetch などを使用してJestを動かしていたと思いますが、
今回から undiciを使用するのでアンインストールしちゃっても良さそうです(自分はしました)
jest.setup.jsのimport文も忘れずに削除

jest.config.jsのsetupFilesに追加したpolyfillsを追記

setupFilesAfterEnvではなくsetupFilesに追記してください(1敗)

const config = {
  setupFiles: ['./jest.polyfills.js'],
~~~~~~~~~
}

一度動かしてみる

一度動かしてみましょう
動きましたか?あなたは幸運ですお疲れ様でした!

エラーが出た方はもう少しだけ続きます頑張りましょう

ReferenceError: ReadableStream is not definedの場合

jest.polyfills.jsにもう少し設定が必要です
参考issue

jest.polyfills.js
const { TextDecoder, TextEncoder } = require('node:util')
 
Object.defineProperties(globalThis, {
  TextDecoder: { value: TextDecoder },
  TextEncoder: { value: TextEncoder },
})

+const { ReadableStream } = require('node:stream/web')
+Object.defineProperties(globalThis, {
+  ReadableStream: { value: ReadableStream },
+})

const { Blob, File } = require('node:buffer')
const { fetch, Headers, FormData, Request, Response } = require('undici')

Object.defineProperties(globalThis, {
  fetch: { value: fetch, writable: true },
  Blob: { value: Blob },
  File: { value: File },
  Headers: { value: Headers },
  FormData: { value: FormData },
  Request: { value: Request },
  Response: { value: Response },
})

TypeError: Failed to execute 'readAsArrayBuffer' on 'FileReader': parameter 1 is not of type 'Blob'.の場合

jest.polyfills.jsにFileReaderを追加してあげる

jest.polyfills.js
const { TextDecoder, TextEncoder } = require('node:util')
 
Object.defineProperties(globalThis, {
  TextDecoder: { value: TextDecoder },
  TextEncoder: { value: TextEncoder },
})

const { ReadableStream } = require('node:stream/web')
Object.defineProperties(globalThis, {
  ReadableStream: { value: ReadableStream },
})

const { Blob, File } = require('node:buffer')
-const { fetch, Headers, FormData, Request, Response } = require('undici')
+const { fetch, Headers, FormData, Request, Response, FileReader } = require('undici')

Object.defineProperties(globalThis, {
  fetch: { value: fetch, writable: true },
  Blob: { value: Blob },
  File: { value: File },
  Headers: { value: Headers },
  FormData: { value: FormData },
  Request: { value: Request },
  Response: { value: Response },
+  FileReader: { value: FileReader },
})

Javascript heap memoryの場合

もしかしてあなたは jest.useFakeTimers() を使用されていますか?

デフォルトのまま使用するとNode.js のfetchで内部的に使用されているrequest/response
が正常に解決されなくなってしまうため

jest.useFakeTimers({ legacyFakeTimers: true })
jest.useFakeTimers({ doNotFake: ['queueMicrotask'] }) と設定してあげるとうまくいくかもしれません!

issue
Requests are not resolving when using jest.useFakeTimers

まとめ

自分は幸い(?)上記対応のみで今まで通りテストが動くようになりました
おそらくこちらに記載のないエラーが出ることもあると思います

そういった場合は
jest.polyfills.jsベースで一度追加できないか考えてみるとissueのない事象も解決できるかもしれません

これを見た誰かの役に立てれば幸いです

最後にmswという素晴らしいライブラリを開発してくださっている方々に感謝を
これほど大きな変更を行ったことはとても大変だったと思います
まだまだ複合的な理由で動かなくなってしまうことなどは有りそうですがこれからもmswの
進化に期待していきたいと思います

Discussion