swaggerを用いて JMeter のテストシナリオを作成する

swagger を用いると、API の仕様をブラウザで確認できるだけでなく、様々なコードの自動生成を行うことにより開発効率を向上させることができます。

ここでは、swagger ファイルから JMeter のテストシナリオを自動生成して、負荷試験を行う環境を用意してみます。


# swagger-codegen jmeter
## overview
https://github.com/swagger-api/swagger-codegen/blob/master/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JMeterCodegen.java

swagger-codegen の jmeter 生成用の実装は上記になります。行うことは比較的シンプルで、以下の２ファイルの生成を行います。

- xxx.jmx : テストシナリオファイル
- xxx.csv : テストに使用するパラメータファイル

## generate
swagger-codegen の実行は、以下のように行います。事前に swagger-codegen などのインストール・設定は済ませた上で実施してください。

```bash

java -jar swagger-codegen-cli.jar generate  \
  -i swagger.json  \
  -l jmeter \
  -o ./jmeter \
```

 -o オプションで指定したディレクトリに、テストシナリオとCSVファイルが生成されます。

## sample file

```
{
  "swagger": "2.0",
  "info": {
    "description": "Api Documentation",
    "version": "1.0.0",
    "title": "Api Documentation",
    "termsOfService": "urn:tos",
    "contact": {},
    "license": {
      "name": "Apache 2.0",
      "url": "http://www.apache.org/licenses/LICENSE-2.0"
    }
  },
  "host": "localhost:8080",
  "basePath": "/",
  "tags": [
    {
      "name": "sada-controller",
      "description": "Sada Controller"
    }
  ],
  "paths": {
    "/sada": {
      "get": {
        "tags": [
          "sada-controller"
        ],
        "summary": "sada",
        "operationId": "sadaUsingGET",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "*/*"
        ],
        "parameters": [
          {
            "name": "echo",
            "in": "query",
            "description": "echo",
            "required": false,
            "type": "string",
            "default": "masashi"
          }
        ],
        "responses": {
          "200": {
            "description": "echo",
            "schema": {
              "type": "string"
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    }
  }
}
```

上のサンプル JSON を用いて、実際に swagger-codegen 実行してみます。実行すると以下のファイル群が生成されます。

- .swagger-codegen/
- .swagger-codegen-ignore
- SadacontrollerApi.csv
- SadacontrollerApi.jmx

### jmx 
テストシナリオの雛形です。

接続先のサーバー（host: localhost） や port 番号（port: 8080）は仮のものになっているので、必要に応じてファイルを修正するか、実行時にパラメータとして渡して指定します。

負荷試験時に使用するパラメータは、後述の CSV ファイルを読み込んで指定するようになっています。こちらも手直しが必要であれば手直しをします。

```xml

<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="2.8" jmeter="2.13 r1665067">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="SadacontrollerApi Test Plan" enabled="true">
      <stringProp name="TestPlan.comments"></stringProp>
      <boolProp name="TestPlan.functional_mode">false</boolProp>
      <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
      <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
        <collectionProp name="Arguments.arguments">
          <elementProp name="host" elementType="Argument">
            <stringProp name="Argument.name">host</stringProp>
            <stringProp name="Argument.value">localhost</stringProp>
            <stringProp name="Argument.metadata">=</stringProp>
          </elementProp>
          <elementProp name="port" elementType="Argument">
            <stringProp name="Argument.name">port</stringProp>
            <stringProp name="Argument.value">8080</stringProp>
            <stringProp name="Argument.metadata">=</stringProp>
          </elementProp>
        </collectionProp>
      </elementProp>
      <stringProp name="TestPlan.user_define_classpath"></stringProp>
    </TestPlan>
    <hashTree>
      <Arguments guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
        <collectionProp name="Arguments.arguments">
          <elementProp name="testCases" elementType="Argument">
            <stringProp name="Argument.name">testCases</stringProp>
            <stringProp name="Argument.value">${__P(testCases,10)}</stringProp>
            <stringProp name="Argument.metadata">=</stringProp>
(snip.)
```

### csv
テストデータのCSVファイルです。

渡すべき項目についてはある程度網羅されていますが、実際に渡すパラメータはかなり適当なものが含まれているので、こちらについては手で望ましいものに修正します。

```
testCase,httpStatusCode,echo
Success,200,0
```

# JMeter
## JMeter install

テストシナリオの手直しを終えたら、実際に Jmeter を動かしてみます。

JMeter のインストールは、Ubuntu 環境であれば apt-get 、 Mac であれば brew コマンドで行えます。

```
# ubuntu
$ sudo apt-get install -y jmeter
# mac
$ brew install jmeter
```

## JMeter run
コマンドラインでは、以下のようなコマンドで実行します。

```
jmeter -n -t (jmx file) \
             -l sada.jtl \
             -e -o report \
             -Jhost=1.2.3.4 \
             -Jport=80
```

- -n : コマンドラインモード(Non GUI モード)で起動
- -t : テストシナリオファイル(jmx) を指定
- -l : テストの実行結果を出力するファイル(jtl) を指定
- -e : HTML レポートを出力
- -o : レポートファイルの出力先ディレクトリを指定
- -Jxxx : jmx ファイル中に記載されている Property の設定・上書きを行う。 -Jhost であれば host という property の値を渡した値で上書きする。

以下は実行例です。

```bash

$ jmeter -n -t ./bin/jmeter/SadacontrollerApi.jmx -l sada.jtl -e -o report -Jhost=x.x.x.x -Jport=80
Creating summariser <summary>
Created the tree successfully using ./bin/jmeter/SadacontrollerApi.jmx
Starting the test @ Mon Apr 09 12:12:00 JST 2018 (1523243520009)
Waiting for possible Shutdown/StopTestNow/Heapdump message on port 4445
summary +      1 in 00:00:01 =    1.9/s Avg:   103 Min:   103 Max:   103 Err:     0 (0.00%) Active: 1 Started: 1 Finished: 0
summary +      9 in 00:00:00 =   85.7/s Avg:    10 Min:     8 Max:    15 Err:     0 (0.00%) Active: 0 Started: 1 Finished: 1
summary =     10 in 00:00:01 =   15.6/s Avg:    19 Min:     8 Max:   103 Err:     0 (0.00%)
Tidying up ...    @ Mon Apr 09 12:12:01 JST 2018 (1523243521083)
... end of run
```

## JMeter result
上述のコマンドで実行した場合、結果は以下２種類のファイルで出力されます。

- jtl ファイル
- HTML レポート


### jtl ファイル
JMeter によるテストの実行ログが出力されます。

```text

timeStamp,elapsed,label,responseCode,responseMessage,threadName,dataType,success,failureMessage,bytes,sentBytes,grpThreads,allThreads,Latency,IdleTime,Connect
1523242680196,97,sadaUsingGET - Success,200,,Thread Group - sadaUsingGET 1-1,text,true,,197,127,1,1,96,0,65
1523242680300,9,sadaUsingGET - Success,200,,Thread Group - sadaUsingGET 1-1,text,true,,197,127,1,1,9,0,0
(snip.)
```

これで各シナリオでどのような動きになったか詳細が終えますが、よりサマライズされた結果を見たい場合は HTML レポートを見ます。


### HTML レポート
-o オプションで指定したディレクトリに HTML ファイルが出力されます。こちらをブラウザで開くと、Jmeter の実行結果をグラフ等々でよりわかりやすくビジュアライズすることができます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/44364/181d202e-d86f-fe6c-4689-4d6e233a2757.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/44364/c1f9681f-a7ca-fd93-307b-e070c6c77695.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/44364/5dcd5b41-689f-6cee-e930-1df8fcdbe24c.png)

# 課題
swagger-codegen による JMeter のテストシナリオ自動生成は、ある程度の雛形までは自動的に作ってくれますが、実行時に用いるパラメータなど、自動生成されたものから色々とチューニングを行う必要があります。


とはいえ、どうしてもテスト設計者の思いつきでシナリオが作られやすく、かつメンテナンスがされず使い捨て的にテストが管理されることが多いのが負荷テストを取り巻く現状かなと認識しています。

このように Swagger を起点としてこのようなプロセスである程度のところまで自動生成する方が、より手間も少なくなりますし、人間が本来しなくても良い作業が不要になりますし、属人性が排除できると思います。


# まとめにかえて
Swagger で API 仕様を表現する、という開発規約をきちんと用意することで、API 仕様を明示化するという効能以上に、様々な開発効率を向上させる施策を開発プロジェクトにもたらすことが可能になります。

その一例として、Swagger を用いて JMeter のテストシナリオを作成してみました。
他にも用途が多いので、API の仕様は Swagger (Open API) で管理していきたいですね。