TL;DR
axiosそのものではなく、adapter
でMockすると良さそう
背景
axiosを使ったコードのテストをJestで書くときにMock化したい。
よくあるサンプルにはaxiosモジュールそのものをMockにしている例がある。
ただし、下記のようにaxios.create()
やaxios.defaults
を使って共通設定している場合、
import axios from 'axios'
const api = axios.create({
...
})
export const getFoo = req => api.post('/foo', req)
axiosモジュールそのものをMockにすると、create関数の戻り値でさらにMockを返すなどの実装がテストコード側で必要になりかなり面倒。
さらに、interceptorsやtransformersなんかを設定している場合には、最終的にサーバーにどんなリクエストが飛ぶかをテストしたい場合も出てくる。
解決策
axiosを乗りこなす機能についての知見集 - Qiita を見て知ったのだけど、adapter
プロパティを使うとRequest, ResponseがMockできて良い感じだった。
使い方や詳細については下記も参照。
axios/lib/adapters at master · axios/axios
実装サンプル
GitHubにもあります。
clomie/sample-axios-jest-mock-test
プロダクトコード側はMockをインポートしてadapter
に渡す実装にしておく。
import axios from 'axios'
import { stringify } from 'querystring'
import mockAdapter from './mock'
const api = axios.create({
baseURL: 'https://api.example.com/v2/',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
adapter: mockAdapter
})
export const createFoo = async (req: any) => {
const { data } = await api.post('/foo', stringify(req))
return data
}
プロダクトコード側のMockの中身はundefined
にしておけば良い。
import { AxiosAdapter } from 'axios'
export = (undefined as unknown) as AxiosAdapter
テストコード側でJestのMockに差し替えてテストを書いていく。
import { AxiosAdapter } from 'axios'
jest.mock('./mock', () => jest.fn())
import mock from './mock'
const axiosMockAdapter = (mock as unknown) as jest.Mock<
ReturnType<AxiosAdapter>,
Parameters<AxiosAdapter>
>
import { createFoo } from './index'
describe('createFoo test', () => {
beforeEach(() => {
axiosMockAdapter.mockClear()
})
it('send form request and receive response', async () => {
const request = {
name: 'hogefuga'
}
const response = {
status: 200,
statusText: 'OK',
headers: {},
config: {},
data: {
id: 1,
name: 'hogefuga'
}
}
axiosMockAdapter.mockResolvedValueOnce(response)
const result = await createFoo(request)
const callArgs = axiosMockAdapter.mock.calls[0]
expect(callArgs[0].url).toBe('https://api.example.com/v2/foo')
expect(callArgs[0].headers).toHaveProperty(
'Content-Type',
'application/x-www-form-urlencoded'
)
expect(callArgs[0].data).toBe('name=hogefuga')
expect(result).toStrictEqual({ id: 1, name: 'hogefuga' })
})
})