こんにちは。TIG DXユニット 1 
はじめに みなさんSwagger使ってますか?
当社でもREST APIを構築するに当たってSwaggerを導入する機会が増えています。本記事ではSwaggerを導入するに当たって、合わせて利用して便利だったツールを紹介したいと思います。
そもそもSwaggerとは? Swaggerは、OpenAPI仕様 (以下OAS)と言われる、REST APIを定義するための標準仕様にもとづいて構築された一連のオープンソースツールです。REST APIの設計、構築、文書化、および使用に役立つ機能を提供します。
提供されている主なツールは次のようなものがあります。
サードパーティ製のツール 本家からは上述のツールが提供されていますが、サードバーティ製の様々なツールが世の中には存在します。
エコシステムが成熟しているのもSwaggerを利用するメリットの1つですね。https://openapi.tools/ 
冒頭のとおり、このサードパーティ製のツールの中で実際に利用して良かったツールを3つご紹介したいと思います。
Stoplight Studio https://stoplight.io/studio/ 
1つ目のツールは「Stoplight Studio」というAPI仕様を記載するためのGUIエディタとなります。
今までSwagger Editorを利用してYAMLを書いていたそこのみなさん、YAML筋力はもう必要ありません。
Design APIs 10x faster の謳い文句どおり、Stoplight Studioを使えばGUIで直感的に、高速にAPI仕様を記述できます。
主な特徴 主な特徴としては次のようなものが挙げられます。
無料 
Web、バイナリ(Windows、Mac、Linux)として配布 
OpenAPI v2 & v3に対応 
Git連携 
Prismと呼ばれるモックサーバ(後述)を統合 
ドキュメントへの変換に対応 
リアルタイムでLintエラーを表示 
 
筆者はもうこのエディタなしではSwaggerを書けない体になりました。Webから簡単に試すことができるので実際に使ってみるのが一番だと思います。https://stoplight.io/p/studio/gh/stoplightio/studio 
Prism https://stoplight.io/open-source/prism 
2つ目のツールは「Prism」というStoplight Studioと同じくStoplight社が提供するOSSのモックサーバです。
コマンドラインからOAS定義を読み込むことで簡単にAPIのモックサーバが起動できます。例えばサーバ(API)側ができていない状態で、クライアント側の開発を進めるケースなどでは非常に有用ですね。
主な特徴 主な特徴としては次のようなものが挙げられます。
OSS 
OpenAPI v2 & v3に対応 
Nodeモジュール、バイナリ(Windows、Mac、Linux)、Dockerイメージとして配布 
ダイナミックレスポンス対応 
リクエストのバリデーション対応 
CORS対応 
 
apisproutなど他のモックサーバも多数存在しますが、ランタイムなしで利用できる点やダイナミックレスポンス、CORS対応等、地味に嬉しい機能があり、お気に入りです。
使ってみた 今回はDockerイメージ利用してみます。 サンプルのOAS定義としてSwagger Petstore を利用します。定義内容はこちら 。
まずはヘルプコマンド 利用可能なオプションは次のとおりとなります。
$ docker run stoplight/prism:3 mock -h prism mock <spec> Start a mock server with the given spec file Positionals:   spec  Path to a spec file. Can be both a file or a fetchable resource on the web.  [string] [required] Options:   --version           Show version number  [boolean]   --help               Show help   [boolean]   --port, -p          Port that Prism will run on.  [number] [required] [default: 4010]   --host, -h          Host that Prism will listen to.  [string] [required] [default: "127.0.0.1" ]   --dynamic, -d       Dynamically generate examples.  [boolean] [default: false ]   --cors              Enables CORS headers.  [boolean] [default: true ]   --multiprocess, -m  Forks the http server from the CLI for  faster log  processing.  [boolean] [default: true ] 
サーバ起動 引数にOAS定義を指定して prism mock コマンドを実行するとモックサーバが立ち上がります。
docker run --rm  -it -p 4010:4010 stoplight/prism:3 mock -h 0.0.0.0 https://petstore.swagger.io/v2/swagger.json [CLI] …  awaiting  Starting Prism… [HTTP SERVER] ℹ  info      Server listening at http://0.0.0.0:4010 [CLI] ●  note      POST       http://0.0.0.0:4010/pet [CLI] ●  note      PUT        http://0.0.0.0:4010/pet [CLI] ●  note      GET        http://0.0.0.0:4010/pet/findByStatus [CLI] ●  note      GET        http://0.0.0.0:4010/pet/findByTags [CLI] ●  note      GET        http://0.0.0.0:4010/pet/{petId} [CLI] ●  note      POST       http://0.0.0.0:4010/pet/{petId} [CLI] ●  note      DELETE     http://0.0.0.0:4010/pet/{petId} [CLI] ●  note      POST       http://0.0.0.0:4010/pet/{petId}/uploadImage [CLI] ●  note      GET        http://0.0.0.0:4010/store/inventory [CLI] ●  note      POST       http://0.0.0.0:4010/store/order [CLI] ●  note      GET        http://0.0.0.0:4010/store/order/{orderId} [CLI] ●  note      DELETE     http://0.0.0.0:4010/store/order/{orderId} [CLI] ●  note      POST       http://0.0.0.0:4010/user [CLI] ●  note      POST       http://0.0.0.0:4010/user/createWithArray [CLI] ●  note      POST       http://0.0.0.0:4010/user/createWithList [CLI] ●  note      GET        http://0.0.0.0:4010/user/login [CLI] ●  note      GET        http://0.0.0.0:4010/user/logout [CLI] ●  note      GET        http://0.0.0.0:4010/user/{username} [CLI] ●  note      PUT        http://0.0.0.0:4010/user/{username} [CLI] ●  note      DELETE     http://0.0.0.0:4010/user/{username} [CLI] ▶  start     Prism is listening on http://0.0.0.0:4010 
ローカルから繋いでみます。 定義した通りのレスポンスが返却されていますね。
$ curl -s -D /dev/stderr -X GET -H "Accept:application/json"  http://localhost:4010/pet/0001 | json_pp HTTP/1.1 200 OK access-control-allow-origin: * content-type: application/json; charset=utf-8 content-length: 138 Date: Fri, 27 Sep 2019 10:25:48 GMT Connection: keep-alive {    "status"  : "available" ,    "photoUrls"  : [       "string"     ],    "id"  : 0,    "name"  : "doggie" ,    "category"  : {       "name"  : "string" ,       "id"  : 0    },    "tags"  : [       {          "id"  : 0,          "name"  : "string"        }    ] } 
ダイナミックレスポンス モックサーバ起動時に-dオプションを付与すると、OAS定義にもとづいてリクエストのたびにレスポンスが動的に作成されます。
docker run --rm  -it -p 4010:4010 stoplight/prism:3 mock -h 0.0.0.0 -d https://petstore.swagger.io/v2/swagger.json 
1回目
$ curl -s -D /dev/stderr -X GET -H "Accept:application/json"  http://localhost:4010/pet/0001?hoge=dow | json_pp HTTP/1.1 200 OK access-control-allow-origin: * content-type: application/json; charset=utf-8 content-length: 338 Date: Fri, 27 Sep 2019 10:38:36 GMT Connection: keep-alive {    "tags"  : [       {          "name"  : "aliquip tempor" ,          "id"  : 63211888       },       {          "name"  : "in enim dolor" ,          "id"  : 79883460       },       {          "name"  : "eiusmod " ,          "id"  : 17183756       }    ],    "category"  : {       "name"  : "fugiat" ,       "id"  : -44395203    },    "id"  : -79576346,    "status"  : "available" ,    "name"  : "nostrud" ,    "photoUrls"  : [       "amet eiusmod Duis deserunt sunt" ,       "dolor" ,       "Duis non reprehenderit" ,       "laboris mollit officia consectetur"     ] } 
2回目
$ curl -s -D /dev/stderr -X GET -H "Accept:application/json"  http://localhost:4010/pet/0001?hoge=dow | json_pp HTTP/1.1 200 OK access-control-allow-origin: * content-type: application/json; charset=utf-8 content-length: 274 Date: Fri, 27 Sep 2019 10:38:39 GMT Connection: keep-alive {    "name"  : "ex consequat ea irure" ,    "status"  : "available" ,    "tags"  : [       {          "id"  : 39284365,          "name"  : "elit Duis nostrud"        }    ],    "category"  : {       "id"  : 39510092,       "name"  : "quis veniam ipsum Excepteur"     },    "id"  : 97837350,    "photoUrls"  : [       "reprehenderit exercitation commodo dolore" ,       "consectetur" ,       "sint" ,       "consequat"     ] } 
バリデーション リクエストボディを指定せずに POST: http://0.0.0.0:4010/petを投げてみるとエラーが返却されます。公式のドキュメント を参考にしてみてください。
$ curl -s -D /dev/stderr -X POST -H "Accept:application/json"  http://localhost:4010/pet | json_pp HTTP/1.1 422 Unprocessable Entity access-control-allow-origin: * content-type: application/problem+json content-length: 350 Date: Fri, 27 Sep 2019 10:41:23 GMT Connection: keep-alive {    "validation"  : [       {          "message"  : "Body parameter is required" ,          "severity"  : "Error" ,          "code"  : "required"        }    ],    "detail"  : "Your request body is not valid and no HTTP validation response was found in the spec, so Prism is generating this error for you." ,    "type"  : "https://stoplight.io/prism/errors#UNPROCESSABLE_ENTITY" ,    "title"  : "Invalid request body payload" ,    "status"  : 422 } 
CORS Prismはデフォルトで、全てのメソッドと全てのオリジンを許可するため、全てのプリフライトリクエストを204でハンドリングします。
ローカルで(Webpack Dev Server等を利用して)Web開発をしているときに、プロキシを設定したりしなくて済むのは、嬉しいですね。
Dredd https://dredd.readthedocs.io 
最後に紹介するツールは「Dredd」というOAS定義と実際のAPIサーバの検証を行うコマンドラインベースのテストツールになります。
要はAPIのレスポンスがOAS定義通りだよね? というのを確認してくれるツールです。もともとはAPI Blueprintに対応していたツールですが、OpenAPIにも対応がなされました。
主な特徴 主な特徴としては次のようなものが挙げられます。
OpenAPI v2 & v3に対応(ただしv3はExperimental) 
Nodeモジュール、Dockerイメージとして配布 
テスト時の前処理、後処理をさまざまな言語(Go, Node.js, Perl, Python, Ruby, etc…)で定義可能 
 
使ってみた テスト対象のAPIサーバはlocalhost:4010で動いている前提とします。
テスト仕様書となるOAS定義として今回もSwagger Petstoreを利用したいところですが、そのまま利用するには色々と問題 があるみたいなので、Petstoreを修正した簡易版のOAS定義を作成し利用します。
IDをキーにペットを取得するAPI、更新するAPIの2APIを定義しています。
GET : /pet/{petId}POST: /pet/${petId} 
swagger.yml swagger:  "2.0" info:   title:  Swagger  Petstore    version:  1.0 .2  schemes:   -  http  paths:   "/pet/{petId}" :      get:        summary:  Find  a  pet  by  ID        description:  Returns  a  single  pet        operationId:  getPetById        produces:          -  application/json;  charset=utf-8        parameters:          -  name:  petId            in:  path            required:  true            type:  integer            format:  int64            x-example:  99999        responses:          "200":            description:  successful  operation            schema:              "$ref" :  "#/definitions/Pet"      post:        summary:  Update  a  pet  by  ID        description:  ""        operationId:  updatePet        consumes:          -  application/json        produces:          -  application/json;  charset=utf-8        parameters:          -  name:  petId            in:  path            required:  true            type:  integer            format:  int64            x-example:  99999          -  name:  body            in:  body            required:  true            schema:              type:  object              properties:                name:                  type:  string                  example:  pochi        responses:          "200":            description:  successful  operation            schema:              "$ref" :  "#/definitions/Pet"  definitions:   Pet:      type:  object      required:        -  name        -  photoUrls      properties:        id:          type:  integer          format:  int64          example:  99999        name:          type:  string          example:  doggie        photoUrls:          type:  array          items:            type:  string            example:  http://example.com        status:          type:  string          enum:            -  available            -  pending            -  sold          example:  sold  
正常系 OAS定義と実際のAPIサーバのホストを引き数にdredd コマンドを実行すると2本のAPIのリクエストが投げられ、レスポンスが検証されます。
$ dredd swagger.yaml localhost:4010 -h "Accept:application/json"  pass: GET (200) /pet/99999 duration: 26ms pass: POST (200) /pet/99999 duration: 9ms complete: 2 passing, 0 failing, 0 errors, 0 skipped, 2 total complete: Tests took 38ms 
異常系 テスト対象のAPIサーバのロジックを修正し、OAS定義と異なるレスポンスを返却するようにしてみましょう。statusがavailable pending soldのいずれのenum値にも当てはまらない値(hoge)を返します。
fail: body: At '/status' No enum match for: "hoge"とログが出力され、期待通りテストが失敗していますね。
$ dredd swagger.yml localhost:4010 -h "Accept:application/json"  fail: GET (200) /pet/99999 duration: 29ms pass: POST (200) /pet/99999 duration: 11ms info: Displaying failed tests... fail: GET (200) /pet/99999 duration: 29ms fail: body: At '/status'  No enum match for : "hoge"  request: method: GET uri: /pet/99999 headers:     Accept: application/json     User-Agent: Dredd/12.0.3 (Darwin 18.2.0; x64) body: expected: headers:     Content-Type: application/json; charset=utf-8 body: {   "name" : "doggie" ,   "photoUrls" : [     "http://example.com"    ],   "id" : 99999,   "status" : "sold"  } statusCode: 200 bodySchema: {"type" :"object" ,"required" :["name" ,"photoUrls" ],"properties" :{"id" :{"type" :"integer" ,"format" :"int64" ,"examples" :[99999]},"name" :{"type" :"string" ,"examples" :["doggie" ]},"photoUrls" :{"type" :"array" ,"items" :{"type" :"string" ,"examples" :["http://example.com" ]}},"status" :{"type" :"string" ,"enum" :["available" ,"pending" ,"sold" ],"examples" :["sold" ]}}} actual: statusCode: 200 headers:     access-control-allow-origin: *     content-type: application/json; charset=utf-8     content-length: 79     date : Sat, 28 Sep 2019 05:20:01 GMT     connection: close bodyEncoding: utf-8 body: {   "id" : 99999,   "name" : "doggie" ,   "photoUrls" : [     "http://example.com"    ],   "status" : "hoge"  } complete: 1 passing, 1 failing, 0 errors, 0 skipped, 2 total complete: Tests took 44ms 
このようにDreddを利用すれば、実際のAPIサーバがOAS定義に則ったレスポンスを返却しているかを検証できます。
もともとAPI Blueprint用のツールだったこともあり、OpenAPIの扱いで筋力が必要なシーンが少なからずありますが、このあたりの泥臭い話は別途記載できればと思います。
各種ツールの統合 標準的な設計・開発プロセスにご紹介したツールを統合すると次のような形になります。
みなさんもクライアントサイドとサーバサイドの結合テストにおいてインタフェースの齟齬による苦労をした経験はあるかと思います。
OAS定義を一元管理し、prismやDreddを効果的に利用することでこのようなコストを大幅に削減でき、品質を強化できます。
ご参考になれば幸いです。
このスキーマファースト開発のためのOpenAPI(Swagger)設計規約  記事もおすすめです。