kurosame’s diary

フロントエンドが得意です

moxios を廃止して Jest.Mock に移行する

JS/TS のユニットテストで axios をモックするのになんとなくmoxiosを使っていたが、やめようかなという話

理由は

  • moxios が 3 年以上更新されていない
    • Issues の返信や Dependabot のプルリクも放置されているっぽい
  • moxios.stubRequestした axios のリクエストがたまに返ってこない時がある
    • ある特定の URL でmoxios.stubRequest(URL, ...だと Jest から axios を実行した際、then に入ってこない時があるので、moxios.stubRequest(new RegExp(URL), ...という謎対応で回避している
    • この辺のコードが怪しいけど、調べてない
  • そもそも moxios のような高機能なライブラリは必要なさそうなテストケースだった
    • axios がPromise.resolvePromise.rejectを返すようにモック化すれば、テストケース的には事足りる

テストフレームワークに Jest を使っているので、Jest のモック機能だけでいけそう

また、1 つの関数内で複数回 axios を実行したりしていないので、テストコードが複雑になることもないと思う

Jest のモック機能へ移行

以下の 2 行でaxios.getPromise.resolveを返すようにモックできる

jest.mock('axios')
axios.get.mockResolvedValue({ data: { name: 'test' } })

JS ならこれでオッケー、ただし TS だとaxios.getに mockResolvedValue は存在しないので、エラーになる
以下のようにすればエラーも消えて、型推論も効くようになる

;(axios as jest.Mocked<typeof axios>).get.mockResolvedValue({
  data: { name: 'test' }
})

Jest の設定

毎回モック設定を書くのは冗長なので、以下の設定をしておく

// test/unit/setup.ts
import axios from 'axios'

jest.mock('axios')

export default axios as jest.Mocked<typeof axios>
// jest.config.js
setupFiles: ['<rootDir>/test/unit/setup.ts'],

setupFiles にテストを実行する前に実行してほしい処理を書いておく

// テストコード
import mockAxios from '@test/setup'

// Promise.resolve
mockAxios.get.mockResolvedValue({ data: { name: 'test' } })
// Promise.reject
mockAxios.get.mockRejectedValue({ response: { status: 400 } })

また、moxios.waitを使って非同期処理を調整していた場合、すべてasync/awaitなどに置き換える必要がある
moxios.waitは内部的に setTimeout 関数を呼んでいるだけなので、そもそもasync/awaitに置き換えた方がよい