Posted at

Jestでデフォルト値設定済みのaxiosインスタンスに対するMockテスト


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に渡す実装にしておく。


indes.ts

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にしておけば良い。


mock/index.ts

import { AxiosAdapter } from 'axios'

export = (undefined as unknown) as AxiosAdapter

テストコード側でJestのMockに差し替えてテストを書いていく。


index.test.ts

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' })
})
})