PostmanとNewmanを用いたAPIレスポンスの保存方法 メモ

* PostmanとNewmanを用いて、複数のAPIリクエストを行い、それらのリクエストに対応付けてレスポンス内容を保存する方法をメモする。

  * 次のようなリクエストとレスポンスから連結データのようなファイルを作りたかった。

    * リクエスト

      ```http
      POST /user/ HTTP/1.1
      Host: localhost:5000
      Content-Type: application/json
      Content-Length: 75
      
      {
        "email_address": "XYZ@example.com",
        "password": "P@ssw0rd1"
      }
      ```

    * レスポンス

      ```json
      {
          "id": "4652d622f8264f628ac9c7c8122b153e"
      }
      ```

    * 連結データ

      ```csv
      email_address,password,id
      XYZ@example.com,P@ssw0rd1,d71aa50e7ae74f039efe607a9eec634c
      ABCXYZ@example.com,P@ssw0rd2,b770407dd85045c79a365f692ad65356
      ```

      

## Newman

* APIリクエスト用ツールPostmanのAPIリクエストjsonファイルをCLIで実行するためのツール



## 準備

### テスト用APIをFastAPIで作成・起動する

* テスト用API`main.py`

  メールアドレス/パスワードを受け取り、IDを返却する。

  ```python
  from fastapi import FastAPI
  from pydantic import BaseModel
  from typing import List
  import uuid
  app = FastAPI()
  
  class User(BaseModel):
      email_address: str
      password: str
  
  @app.post("/user/")
  def create_user(user: User):
      return {"id": str(uuid.uuid4()).replace("-","")}
  ```

* 起動

  ```
  uvicorn main:app --reload --port 5000
  ```

* リクエスト

  ```http
  POST /user/ HTTP/1.1
  Host: localhost:5000
  Content-Type: application/json
  Content-Length: 75
  
  {
    "email_address": "XYZ@example.com",
    "password": "P@ssw0rd1"
  }
  ```

* レスポンス

  ```
  {
      "id": "4652d622f8264f628ac9c7c8122b153e"
  }
  ```

  

### Postmanでリクエストファイルを作成する

![postman.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586535/64beb105-0942-a6c7-a826-7bc59bad3e1a.png)


* リクエスト用JSON`test_app.postman_collection.json`

GUIで作成したものをエクスポートして取得する。

  ```json
  {
  	"info": {
  		"_postman_id": "a5eae803-359e-41ed-b3ac-72b896b859b0",
  		"name": "test_app",
  		"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  	},
  	"item": [
  		{
  			"name": "POST_users",
  			"event": [
  				{
  					"listen": "test",
  					"script": {
  						"exec": [
  							""
  						],
  						"type": "text/javascript"
  					}
  				}
  			],
  			"request": {
  				"method": "POST",
  				"header": [],
  				"body": {
  					"mode": "raw",
  					"raw": "{\r\n  \"email_address\": \"{{email_address}}\",\r\n  \"password\": \"{{password}}\"\r\n}",
  					"options": {
  						"raw": {
  							"language": "json"
  						}
  					}
  				},
  				"url": {
  					"raw": "http://localhost:5000/user/",
  					"protocol": "http",
  					"host": [
  						"localhost"
  					],
  					"port": "5000",
  					"path": [
  						"user",
  						""
  					]
  				}
  			},
  			"response": []
  		}
  	]
  }
  ```

  

### テスト用データ`test_data.csv`を用意する

* 複数リクエストを行うため、以下のようなテストデータを用意する。

```csv
email_address,password
XYZ@example.com,P@ssw0rd1
ABCXYZ@example.com,P@ssw0rd2
```

### Newman実行コード`test.js`を用意する

* Postmanリクエストファイル`test_app.postman_collection.json`とテストデータファイル`test_data.csv`を読み込み、テスト用APIに対してリクエストを行う。

* テストデータとAPIレスポンス(`id`)を連結したCSVファイルを生成する。

  ```javascript
  const fs = require('fs');
  const Papa = require('papaparse');
  const newman = require('newman');
  
  newman.run({
      // テスト用リクエストファイル
      collection: require('./test_app.postman_collection.json'),
      reporters: 'cli',
      // テストデータファイル
      iterationData: './test_data.csv'
  }, (error) => {
  	if (error) { 
          throw error; 
      }
      console.log('Collection Run Complete.');
  }).on('beforeDone', (error, data) => {
      if (error) {
          throw error;
      }
      // APIレスポンスに含まれる`id`を取得
      const idList = data.summary.run.executions.reduce((a, c) => {
          a[c.cursor.iteration] = JSON.parse(c.response.stream.toString()).id
          return a;
      }, []);  
      // テストデータにAPI実行結果`id`を連結したファイルを作成
      updateCsvFile(idList);
  });
  
  function updateCsvFile(idList) {
      fs.readFile('./test_data.csv', 'utf8', (error, data) => {
          if (error) {
              throw error;
          }
  
          const jsonData = Papa.parse(data, { header: true });
          jsonData.data.map((item, index) => item.id = idList[index]);
  
          const updatedCsv = Papa.unparse(jsonData.data);
          console.log(updatedCsv);
  
          fs.writeFile('./test_data_updated.csv', updatedCsv, (error) => {
              if (error) {
                  throw error;
              }
              console.log('CSV File Updated');
          });
  
      });
  }
  ```

  

## 動作確認

* `test.js`実行

  ```shell
  $ node test.js 
  newman
  
  test_app
  
  Iteration 1/2
  
  → POST_users
    POST http://localhost:5000/user/ [200 OK, 166B, 97ms]
  
  Iteration 2/2
  
  → POST_users
    POST http://localhost:5000/user/ [200 OK, 166B, 18ms]
  
  ┌─────────────────────────┬───────────────────┬──────────────────┐
  │                         │          executed │           failed │
  ├─────────────────────────┼───────────────────┼──────────────────┤
  │              iterations │                 2 │                0 │
  ├─────────────────────────┼───────────────────┼──────────────────┤
  │                requests │                 2 │                0 │
  ├─────────────────────────┼───────────────────┼──────────────────┤
  │            test-scripts │                 2 │                0 │
  ├─────────────────────────┼───────────────────┼──────────────────┤
  │      prerequest-scripts │                 0 │                0 │
  ├─────────────────────────┼───────────────────┼──────────────────┤
  │              assertions │                 0 │                0 │
  ├─────────────────────────┴───────────────────┴──────────────────┤
  │ total run duration: 223ms                                      │
  ├────────────────────────────────────────────────────────────────┤
  │ total data received: 82B (approx)                              │
  ├────────────────────────────────────────────────────────────────┤
  │ average response time: 57ms [min: 18ms, max: 97ms, s.d.: 39ms] │
  └────────────────────────────────────────────────────────────────┘
  Collection Run Complete.
  email_address,password,id
  XYZ@example.com,P@ssw0rd1,17f7604dff124376a8341aff71edbdb2
  ABCXYZ@example.com,P@ssw0rd2,c89cb5d7535547d7ad3f2923431c1760
  CSV File Updated
  ```

* 実行結果ファイル`test_data_updated.csv`

  ```csv
  email_address,password,id
  XYZ@example.com,P@ssw0rd1,17f7604dff124376a8341aff71edbdb2
  ABCXYZ@example.com,P@ssw0rd2,c89cb5d7535547d7ad3f2923431c1760
  ```

  

## 参考情報

* [Saving Postman Collection Test Results to a CSV File using Newman](http://blog.geveo.com/Saving-Postman-Collection-Test-Results-to-a-CSV-File-using-Newman)