はじめに
グロースエクスパートナーズ Advent Calendar 2020 3日目。
yitoです。
API開発をしている中で、テストするのに毎回クライアントツールにリクエストを用意してAPIを呼び出すのが手間でした。
それを、コマンド一発で、API仕様書に定義した全てのAPIをテストできるようにしたので、その方法を紹介します。
概要
OpenAPIドキュメントをAPIクライアントツールで利用可能なJSONに変換し、それをCLIライブラリで実行します。
これを実現する、以下について順番に説明していきます。
- Postmanについて
- OpenAPI 3.0 to Postman Collection v2.1.0 Converter
- Postman Collection SDK
- newman
Postmanについて
APIリクエストのクライアントツールにPostmanがあります。
Postmanは、以下のようなことができます。
- 呼び出したいAPIのリクエスト情報を書き出して保存
- => Postman Collections
- APIのテストスクリプト実装
- ステータスコード、レスポンスボディのプロパティのテストなどが可能
- https://learning.postman.com/docs/writing-scripts/test-scripts/
書き出したリクエスト情報を保存すると、画面左側にあるPostman Collectionsに追加され、フォルダでの管理ができます。
このPostman Collectionsは、Webに公開する(制限をかけることも可能?)か、JSONでのexport/importで、開発チーム内で共有が可能です。
OpenAPI 3.0 to Postman Collection v2.1.0 Converter
OpenAPI 3.0 to Postman Collection v2.1.0 Converterは、OpenAPIドキュメントをPostman CollectionのJSONに変換する、NodeJSで実行可能なライブラリです。
使い方は簡単で、OpenAPIのyamlを読み込んで、このライブラリのクラスに渡して実行するだけです。
constfs=require('fs');constreadFileData=fs.readFileSync('OpenAPIドキュメントのファイルパス',{encoding:'UTF8'});constConverter=require('openapi-to-postmanv2');Converter.convert({type:'string',data:readFileData},{},(err,conversionResult)=>{if(!conversionResult.result){console.error('API定義をPostman Collectionに変換できませんでした',conversionResult.reason);return;}constconvertData=conversionResult.output[0].data;// Postman CollectionsのJSONファイルを生成するfs.writeFile('postman-collections.json',JSON.stringify(convertData,null,4),(err)=>{if(err)console.error(err)});});
この生成されたJSONファイルを読み込むと、Postmanにて利用ができます。
ただ、このままだと、各APIのリクエストパラメータが <long>
<string>
のようになっていて、このままだと呼び出したいAPIのパラメータとしては不適切なので、使う際は書き換えが必要です。
手で1つ1つ直すのは面倒なので、Postman Collection SDKを使います
Postman Collection SDK
Postman Collection SDKは、Postman Collectionの生成・更新のための、NodeJSで実行可能なSDKです。
OpenAPI 3.0 to Postman Collection v2.1.0 Converterにて生成したPostman CollectionのJSONを、このSDKを使って編集します。
Postman CollectionのJSONファイルを読み込み、SDKにてCollectionをnewすると、以下のようなクラスが生成されます。
※クラスについての詳細
- Collection
- Postman Collectionの1つに相当
- メンバーにItemGroupを持つ
- ItemGroup
- Postman Collectionの1フォルダに相当
- メンバーにItemを持つ
- Item
- Postman Collectionの1つのAPIに相当
- メンバーにRequestを持つ
- Request
- APIのURL、リクエストパラメータに関する値
- メンバーにHeaderList, EventList, VariableListなどを持つ
<long>
<string>
のような値を実際に使いたい値に変えるため、 Request
の値を書き換えます。
こちらにあるOpenAPIドキュメント(api-spec.yml)、Postman Collection SDKを使った実装(postman-test/index.js)を使いながら説明します。
先ほどの実装に以下を追加します。
...constconvertData=conversionResult.output[0].data;constcollection=newCollection(convertData);collection.items.all().forEach(item=>updateItem(item))// Postman CollectionsのJSONファイルを生成するfs.writeFile('postman-collections.json',JSON.stringify(collection,null,4),(err)=>{if(err)console.error(err)});...
collection.items
に ItemGroup
または Item
があります。そこから、 Request
を取り出して、 <long>
<string>
のような値を、使いたい値に書き換えます。
...const{v4:uuidv4}=require('uuid');constupdateRequestVariable=(request,key,value)=>{constfindVariable=request.url.variables.find(variable=>variable.key===key);if(findVariable){findVariable.update({key:key,value:value})}}constupdateRequestQuery=(request,key,value)=>{constfindQueryParam=request.url.query.find(queryParam=>queryParam.key===key);if(findQueryParam){findQueryParam.update({key:key,value:value})}}constupdateRequest=(request)=>{// Authorizationif(request.auth&&request.auth.type==='apikey'){request.auth.update({"key":"api_key","value":`api-key-${uuidv4()}`},'apikey')}// 書籍関連のAPIif(request.url.getPath().includes('books')){updateRequestVariable(request,'bookId','100000001');}// ユーザー関連のAPIif(request.url.getPath().includes('users')){updateRequestVariable(request,'username','user-XXXX');updateRequestQuery(request,'username','user-XXXX');updateRequestQuery(request,'password','pass-XXXX');}returnrequest;}constupdateItem=(item)=>{if(item.items){item.items.all().forEach(item=>updateItem(item));}else{item.request=updateRequest(item.request);}returnitem;}...
レスポンスに対するテストも実行したいので、 updateItem
メソッドに以下のコードを追加します。
...constupdateItem=(item)=>{if(item.items){item.items.all().forEach(item=>updateItem(item));}else{// 追加item.events.add({listen:'test',script:{exec:"pm.test('response 200 test', () => {\n"+" pm.response.to.have.status(200);\n"+"});"},type:'text/javascript'})...
これでリクエストパラメータの書き換えがされた状態で、Postman CollectionのJSONが生成されます。
毎回JSONをimportしてAPIを呼び出すのは面倒なので、CLIだけで実行できるように、newmanを使います.
newman
newmanは、CLIでPostman CollectionのJSONを読み込みAPIをリクエストする、NodeJSで実行可能なライブラリです。
先ほどのコードで生成したJSONを、newmanで実行するように変えます。
...constcollection=newCollection(convertData);collection.items.all().forEach(item=>updateItem(item))fs.writeFile('postman-collections.json',JSON.stringify(collection,null,4),(err)=>{if(err)console.error(err)});newman.run({collection:collection,reporters:'cli',environment:require('./local.postman_environment.json')},(err)=>{if(err)console.error(err);});...
newman.run()
- collection: Postman CollectionのJSONを指定
- reports: 実行結果の出力方法の指定。CLI、Junit、JSON等が可能
- envioroment: Postmanには環境ごとの変数の指定があり、生成されたJSONには
baseUrl
の指定が必要なため、向き先を指定したJSONを用意して読み込む
実行すると以下のようになります。
% npm run start
>node index.js
newman
Book Management
❏ books
↳ 書籍更新API
PUT http://localhost:8080/v1/books [200 OK, 123B, 235ms]
✓ response 200 test
↳ 書籍登録API
POST http://localhost:8080/v1/books [200 OK, 123B, 19ms]
✓ response 200 test
↳ 書籍一覧取得API
GET http://localhost:8080/v1/books [200 OK, 800B, 11ms]
✓ response 200 test
↳ タグ絞り込み検索API
GET http://localhost:8080/v1/books/findByTags?tags=<string>&tags=<string> [200 OK, 800B, 14ms]
✓ response 200 test
❏ books / {book Id}
↳ 書籍詳細取得API
GET http://localhost:8080/v1/books/100000001 [200 OK, 290B, 10ms]
✓ response 200 test
↳ 書籍削除API
DELETE http://localhost:8080/v1/books/100000001 [200 OK, 123B, 7ms]
✓ response 200 test
❏ users
↳ ユーザー登録API
POST http://localhost:8080/v1/users [200 OK, 123B, 8ms]
✓ response 200 test
↳ ユーザー一覧取得API
GET http://localhost:8080/v1/users [200 OK, 123B, 6ms]
✓ response 200 test
↳ ログインAPI
GET http://localhost:8080/v1/users/login?username=user-XXXX&password=pass-XXXX [200 OK, 123B, 8ms]
✓ response 200 test
↳ ログアウトAPI
GET http://localhost:8080/v1/users/logout [200 OK, 123B, 6ms]
✓ response 200 test
❏ users / {username}
↳ ユーザー詳細取得APi
GET http://localhost:8080/v1/users/user-XXXX [200 OK, 123B, 6ms]
✓ response 200 test
↳ ユーザー更新API
PUT http://localhost:8080/v1/users/user-XXXX [200 OK, 123B, 5ms]
✓ response 200 test
↳ ユーザー削除API
DELETE http://localhost:8080/v1/users/user-XXXX [200 OK, 123B, 7ms]
✓ response 200 test
→ タグ一覧取得API
GET http://localhost:8080/v1/tags [200 OK, 123B, 6ms]
✓ response 200 test
┌─────────────────────────┬───────────────────┬──────────────────┐
│ │ executed │ failed │
├─────────────────────────┼───────────────────┼──────────────────┤
│ iterations │ 1 │ 0 │
├─────────────────────────┼───────────────────┼──────────────────┤
│ requests │ 14 │ 0 │
├─────────────────────────┼───────────────────┼──────────────────┤
│ test-scripts │ 14 │ 0 │
├─────────────────────────┼───────────────────┼──────────────────┤
│ prerequest-scripts │ 0 │ 0 │
├─────────────────────────┼───────────────────┼──────────────────┤
│ assertions │ 14 │ 0 │
├─────────────────────────┴───────────────────┴──────────────────┤
│ total run duration: 654ms │
├────────────────────────────────────────────────────────────────┤
│ total data received: 1.37KB (approx) │
├────────────────────────────────────────────────────────────────┤
│ average response time: 24ms [min: 5ms, max: 235ms, s.d.: 58ms] │
└────────────────────────────────────────────────────────────────┘
以上で、コマンド一発でAPI全てをテストできるようになりました。
参考
- https://learning.postman.com/docs/writing-scripts/test-scripts/
- https://www.postman.com/collection/
- https://github.com/postmanlabs/openapi-to-postman
- https://www.postmanlabs.com/postman-collection/index.html