jsonApiMock - JSONを返すAPIのMockアプリケーション

# Overview
[jsonApiMock](https://github.com/IsaoTakahashi/jsonApiMock)
RESTful APIに限らず、何かしらJSONをレスポンスとして返却するAPIをまとめてモックするアプリケーションを作った。


![jsonApiMock_simpleflow.png](https://qiita-image-store.s3.amazonaws.com/0/72808/e24538af-e8df-2969-506a-f68e7de8b73b.png)

# きっかけ
- いろんな種類のAPIをcallするアプリケーションを作っていて、テストのためにモックAPI作りたいなーと思っていた。
    - が、全種類のモックAPIをそれぞれ作るのは手間がかかりすぎる。
- [json-server](https://github.com/typicode/json-server)はRESTfulなAPIのモックには丁度いい…んだけど、そうじゃないAPIをモックするのは難しい。
    - もっとゆるーく「JSON返すAPI」を対象にしたモックツールが欲しい。
- モック用のデータセット作るのがめんどくさ過ぎる。
    - 「APIの仕様が変わったって？じゃあまたデータ作り直しかい…」
    - "今動いているもの"を手早くモックしたい。

# できること
APIをモックできます。

## 対応するRequest
大抵のHttpRequestをモックできる(GET,POST etc.)

- 実際のAPIをcallする形式で、モックデータを定義できる
- Content-Type, RequestBody, QueryStringもちゃんと識別できる
- 認証情報やCookieについては識別していない

## 対応するResponse
今のところJSONのみ。

- モックのレスポンスとして、HttpStatusとJSONのbodyを定義できる
- 異なるHttpMethod,RequestBody,QueryStringに対して異なるレスポンスを定義できる

## モックデータの作成について
- 記録したモックデータの取得(JSON), およびそのJSONを用いたモックデータの登録ができる
- __実際のAPI呼び出しを盗み見て、モックデータを作成できる__:sunglasses: 

モックデータの形式は↓のような感じ。
リクエストの同一性は、request内の各fieldの値が一致しているかどうかでチェックしている。
(ただしheaderとかbodyの一部は一致チェックには使用していない)
c.f. [ApiMockRequest.hash](https://github.com/IsaoTakahashi/jsonApiMock/blob/master/src/main/java/itaka/api/mock/bean/ApiMockRequest.java#L43)

```json:sample-mock-data.json
{
    "id": "82b16c33c5060e5d57e9d2111d465c1c",
    "request": {
        "body": "",
        "endpoint": "/users",
        "headers": {
            "accept": "*/*",
            "host": "localhost:8080",
            "user-agent": "curl/7.43.0"
        },
        "method": "GET",
        "params": [
            {
                "key": "name",
                "value": "Jiro"
            }
        ]
    },
    "response": {
        "body": "[\n  {\n    \"name\": \"Jiro\",\n    \"id\": 3\n  }\n]",
        "httpStatus": 200
    }
}
```

# 使い方
各機能の詳細は[README](https://github.com/IsaoTakahashi/jsonApiMock/blob/master/README.md)に。
(各機能のサンプルもそちらに)

## 動作環境
- JDK 1.8以降
- Maven 3.x

Spring Bootで組んであるので、使用感はそれに準拠している。はず。

## 起動方法
`mvn spring-boot:run`か、固めたjarを実行

```sh:package_and_run.sh
mvn package
java -jar target/jsonAPIMock.jar
```

## 基本的な利用フロー
トップの図を再掲。
![jsonApiMock_simpleflow.png](https://qiita-image-store.s3.amazonaws.com/0/72808/e24538af-e8df-2969-506a-f68e7de8b73b.png)

1. モックしたいリクエストを投げる  
(`/apimock/request/{mockTargetPath}`)  
   => モックデータのIDが返ってくる
2. 1でモックしたリクエストに対して返却するレスポンス(Body, HttpStatus)を投げる  
  (`/apimock/response/{id}/{responseStatus}`)  
   => 更新したモックデータのIDが返ってくる
3. 実際のAPIをcallするように、jsonApiMockをcallする  
  (`/apimock/execute/{mockTargetPath}`)  
   => 2でモックしたレスポンスが返ってくる

例としては以下のような感じ。

```sh:simple_sample
# /test/hoge というエンドポイントのモックリクエストを登録
curl http://localhost:8080/apimock/request/test/hoge?name=John
##> 3440f2e01a9d2779b2823811b5e5cf9c

# /test/hoge のモックレスポンスを登録
curl -H "Content-type: application/json" -X POST http://localhost:8080/apimock/response/3440f2e01a9d2779b2823811b5e5cf9c/200 -d '
{"greeting" : "hello, John!"}'
##> 3440f2e01a9d2779b2823811b5e5cf9c

# 登録したモックデータを、APIモックとして利用する
curl http://localhost:8080/apimock/execute/test/hoge?name=John
##> {"greeting" : "hello, John!"}
```

## Spyモード :sunglasses: 
jsonApiMockをクライアントアプリとAPIの間に噛ませることで、  
__「実際にAPI呼び出しを行いながら、モックデータを自動記録する」__  
ことができる。

大体こんな↓仕組み。
![jsonApiMock_spymode.png](https://qiita-image-store.s3.amazonaws.com/0/72808/7af60031-62eb-bd37-63dd-fbae346263b9.png)

1．client側が、jsonApiMockの`/spy`エンドポイント経由で実際のAPIをcallする
2. jsonApiMockは、以下の3つの処理を行う
    2-1. client側から受けたリクエスト内容を実際のAPIにリクエストする
    2-2. 実際のAPIのレスポンスを加工せず、client側に返却する
    2-3. リクエスト内容とレスポンス内容をセットにして、モックデータとして記録する
3. client側は、実際のAPIを直接callしたときと同じ使用感で、処理を続行する

具体的な手順は以下の通り。

```sh:spy_sample
# 実際に使用するAPIのパスをセットして起動
java -jar target/jsonAPIMock.jar --apimock.spy-target-url="http://gturnquist-quoters.cfapps.io"

# もしくは、/envエンドポイントを利用して、起動中に動的にパスを変更
curl -X POST http://localhost:8080/apimock/env -d apimock.spy-target-url="http://gturnquist-quoters.cfapps.io"
##> {"apimock.spy-target-url":"http://gturnquist-quoters.cfapps.io"}

# 起動直後なのでモックデータが存在しないことを確認 (Optional)
curl http://localhost:8080/apimock/data
##> []

# /spy に対してAPI Requestを投げる
curl http://localhost:8080/apimock/spy/api/random
##> {"type":"success","value":{"id":12,"quote":"@springboot with @springframework is pure productivity! Who said in #java one has to write double the code than in other langs? #newFavLib"}}
# ↑これは実際のAPI(http://gturnquist-quoters.cfapps.io/api/random)のレスポンスそのもの

# さっきのAPIリクエストについて、データが記録されていることを確認 (Optional)
curl http://localhost:8080/apimock/data
##> [{"id":"89150a098c708c1cfc2c26c03b867902","request":{"endpoint":"/api/random","method":"GET","contentType":null,"body":"","params":[]},"response":{"body":"{\"type\":\"success\",\"value\":{\"id\":12,\"quote\":\"@springboot with @springframework is pure productivity! Who said in #java one has to write double the code than in other langs? #newFavLib\"}}","httpStatus":200}}]

# /execute 経由で、記録したモックデータを利用
curl http://localhost:8080/apimock/execute/api/random
##> {"type":"success","value":{"id":12,"quote":"@springboot with @springframework is pure productivity! Who said in #java one has to write double the code than in other langs? #newFavLib"}}
# ↑このレスポンスは、記録されたデータを返しているだけで、実際のAPIはcallしていない
```

この仕組みがあることで、モックデータの作成がだいぶ楽になると思う。


## その他の機能
いくつか便利エンドポイントも作った。

### /apimock/data [GET]
記録されているモックデータの一覧を取得

### /apimock/data/{id} [GET]
指定したIDのモックデータを取得

### /apimock/data [POST]
モックデータ形式のjsonを受け取って、モックデータとして記録する

### /apimock/dataList [POST]
`/apimock/data`のList版(複数同時登録)

# 今後の展望
心地よく使うために、追加していきたい機能とか。

- モックデータの永続化
    - アプリケーションを終了すると、記録したデータは全て消える。
    - `/apimock/data`で記録済みデータを取得しておいて、次回起動時に`/apimock/dataList`を使って突っ込むというのが現状取りうる運用スタイルになる。
    - json-serverのように都度ファイル書き込み&起動時にファイル読み込みできるようにしておいてもいいかも。 
    
-> (追記) [固定ファイル名でsave/loadするためのEndpointを追加](https://github.com/IsaoTakahashi/jsonApiMock/issues/12)

# まとめ
実際のサービス開発にどれくらいの効果があるのかはまだ分からないけれど、
APIのモック周りで困っている人たちの一助となれるとうれしい。

## 2018-10-02 追記
このツールを作成した直後に[WireMock](http://wiremock.org/)でほとんど同じことが出来るのが分かったので、完全に実装を放置している。
WireMockについては既に多くの記事で言及されているので、APIモックに悩んだらそちらを使う方がオススメである。
(WireMockでも足りないものがある場合、こちらのツールを拡張するかWireMockにContributeする予定)

# 参考文献
json-serverを試す上で、下記の記事を参考にさせていただきました。

- [【個人メモ】JSON Serverを使って手っ取り早くWebAPIのモックアップを作る](http://qiita.com/futoase/items/2859a60c8b240da70572)
- [JSON Server - 簡単にAPIのモックを作成](http://qiita.com/yhikishima/items/46860ffc48913182a3ee)