axios をモックする方法 (React + TypeScript)

### はじめに
HTTPクライアントでお馴染みの [axios](https://github.com/axios/axios) のモック化を試したかったので、
それに関しての導入記事となります。

モックに関しては、 [axios-mock-server](https://github.com/solufa/axios-mock-server) を利用します。

フロントは、 React + TypeScript でサンプルを記述します。

### 環境
- macOS Catalina 10.15.6
- node: v15.3.0
- yarn: 1.22.10
- TypeScript: 4.1.2
- React: 17.0.0
- axios-mock-server: 0.19.0

### 1. パッケージの導入

```
$ yarn add axios
$ yarn add --dev axios-mock-server
```

npm じゃなくて yarn を利用しています。 

### 2. axios を利用した HTTPクライアント の作成
 axios を利用した形の HTTPクライアント部分を実装します。
とりあえず最低限の get/post のみです。

```ts:src/data/rest.ts
import axios from "axios";

type Data = { [key: string]: string | number | boolean | object }

const rest = (() => {
  const client = axios.create({
    baseURL: '', // 今回は未指定 (通常は env などから参照させる)
    timeout: 15000,
  });
  return {
    client,
    get: <T = any, R = AxiosResponse<T>>(url: string): Promise<R> => {
      return client.get(url);
    },
    post: <T = any, R = AxiosResponse<T>>(url: string, data: Data): Promise<R> => {
      return client.post(url, data);
    }
})();

export { rest };
```

この時点ではまだモックを考慮していません。
また、本サンプルでは POST は未使用ですが一応定義しておきます。


### 3. API のインターフェースを定義
今回仮に `User一覧` を返すAPIを想定します。
一旦クエリパラメータは無しで。

|Method|URL|
|---|---|
|GET|`http://localhost:3000/user/list`|

レスポンスの型定義

```ts:src/data/type/index.ts
type User = {
  id: number;
  name: string;
}

export type { User };
```

APIの実装部分

```ts:src/data/api/getUsers.ts
import { rest } from '../rest';
import { User } from '../type';

const getUsers = async (): Promise<User[]> => {
  const url = "/user/list";
  try {
    const { data } = await rest.get<User[]>(url)
    return data;
  } catch (error) {
    throw new Error(error);
  }
}

export { getUsers };
```

コンポーネント側から呼び出す場合はこんな感じです。

```tsx:src/component/Users.tsx
import React, { useEffect } from 'react';
import { getUsers } from '../data';

const Users: React.FC = () => {
  useEffect(() => {
    getUsers().then((users) => {
      console.log('# users', users);
    });
  }, []);
  return <span>Users</span>;
};

export { Users };
```

### 4. mockserverrc の作成
プロジェクトのルート階層（package.jsonがある場所）に .mockserverrc を作成します。
今回 モックAPI のファイルとして、
`/src/data/mock` に格納したいので、そこのパスを指定しています。

```:.mockserverrc
{
  "input": "./src/data/mock"
}
```

### 5. モックAPI の作成

モックAPIを格納するディレクトリは以下の通りです。
（見ての通りディレクトリ構造に沿って、自動的にルーティングが設定されます）

```
├── data
│   └── mock
│       ├── $mock.ts  ... ※自動生成されるファイル （後述で説明)
│       └── user
│           └── list.ts
```

User一覧のモックAPIの実装です。

```ts:src/data/mock/user/list.ts
import { MockMethods, MockResponse } from 'axios-mock-server';
import { User } from '../../type';

const list: MockMethods = {
  get: async (): Promise<MockResponse> => {
    const data: User[] = [
      {
        id: 1,
        name: 'A'
      },
      {
        id: 2,
        name: 'B'
      }
    ];
    return [200, data]; // 200 はステータスコード
  }
};

export default list; // ここは `default export` にしないと動かない
```

### 6. モックAPIをビルドするスクリプトを定義
package.json の `scripts` にモックAPIのビルド設定を追記します。
`-c` で 設定ファイルを指定しています。

```js:package.json
  ...
  "scripts": {
    ...
    "mock:build": "axios-mock-server -c .mockserverrc"
  },
```

### 7. モックAPIのビルド
以下のコマンドを実行します。

```
$ yarn mock:build
```

`$mock.ts` が自動生成されていればOKです。
生成されたコードは以下の通りです。
自動生成されたファイルは、適宜`.gitignore`に追加してください。

```ts:data/mock/$mock.ts
/* eslint-disable */
import { AxiosInstance } from 'axios'
import mockServer from 'axios-mock-server'
import mock0 from './user/list'

export default (client?: AxiosInstance) => mockServer([
  {
    path: '/user/list',
    methods: mock0
  }
], client, '')
```

### 8. axios とモックの紐付け
手順 `2.` で生成した HTTPクライアント に修正を加えます。
自動生成した mock モジュールを `import` し、
関数呼び出しの引数に axios の client を渡しております。

また、お好みで ログ出力 や 遅延設定 は行なえます。

```ts:src/data/rest.ts
import axios, { AxiosResponse } from "axios";
+ import { mock } from './mock';

  ... 省略 ...

+ const useMock = true;
+ if (useMock) {
+   mock(rest.client)
+     .enableLog()
+     .setDelayTime(500);
+ }

export { rest };
```

### 9. 起動コマンドの追加 及び 動作確認
このままだとモックAPIを改修する度に、
`mock:build`コマンドを実行しないといけないので、
独自の起動コマンドを追加します。

package.json の `scripts` に以下を追加します。


```js:package.json
  ...
  "scripts": {
    ...
    "mock:build": "axios-mock-server -c .mockserverrc"
+   "mock:start": "axios-mock-server -c .mockserverrc && react-scripts start"
  },
```
`react-scripts start` は `yarn start` で実行するコマンド内容です。

ローカルでの実行は以下のコマンドとなります。

```
$ yarn mock:start
```

`enableLog()`を有効にしていると、API呼び出し時にログが出力されます。
![スクリーンショット 2020-12-04 16.51.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112186/ec69cdd4-a2c2-916c-2485-88b9686bb15d.png)


### まとめ
以上、axios をモックする方法となります。
サンプルコードは以下にアップしています。
https://github.com/unpii/react-axios-mock-example