Karate-Gatlingによる負荷テストをsbt経由でお手軽に利用しよう

## 初めに：

Karate-GatlingによるE2E/負荷テストをお手軽に実行する。

KarateはE2Eテストフレームワーク、Gatlingは負荷テストフレームワークであり、
両者を組み合わせたものがKarate-Gatlingである。
本来KarateはMaven/Gradleのサポートとなっているが、
今回は、Karate-Gatlingをsbtでも使用可能にする。

【Karateについて】

https://github.com/intuit/karate

https://intuit.github.io/karate/

【Gatlingについて】

https://en.wikipedia.org/wiki/Gatling_(software)

## 通常のKarate(maven)と比較した場合のメリット

* Mavenでは全部設定しなければならない依存関係が自動計算されるため、シンプルになる
* Gatlingのためにscala用依存関係を別途追加していたが、それらは不要になる

## 単体のGatlingと比較した場合のメリット
* Karateによるfeatureファイルが使えることにより、Scalaに詳しくなくてもGatlingが使える
* アサーションの条件が非常にわかりやすい構造になる
* 単なるE2Eテストならばfeatureファイルだけでテストすることもできる(IDEA上で実行可能)

## テスト作成

ディレクトリ配置は、下記の通りに行う
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/161516/61c41fe0-8f42-3e4b-1641-7accc1709773.png)

### テスト作成：ビルド定義

プロジェクト定義は、InteliJ IDEAのsbtテンプレートを使用する。
sbtで動作を可能としたいため、gatling-mavenに変えてgatling-sbtプラグインを使用する

```scala:plugins.sbt
addSbtPlugin("io.gatling" % "gatling-sbt" % "3.2.1")
```

```scala:build.sbt
name := "karate-gatling-sbt"

version := "0.1"

scalaVersion := "2.12.13"
val gatlingVersion = "3.0.2"
val karateVersion = "0.9.6"

//sbt-gatlingがmaven centralに存在しないので、bintrayRepoを追加する
resolvers += Resolver.bintrayRepo("gatling", "sbt-plugins")
enablePlugins(GatlingPlugin)

scalacOptions := Seq(
  "-encoding", "UTF-8", "-target:jvm-1.8", "-deprecation",
  "-feature", "-unchecked", "-language:implicitConversions", "-language:postfixOps")

libraryDependencies ++= Seq(
//  無くても依存関係が計算されて動作する
//  "io.gatling.highcharts" % "gatling-charts-highcharts" % gatlingVersion % "test",
  "io.gatling"            % "gatling-test-framework"    % gatlingVersion % "test",
  "com.intuit.karate"     % "karate-gatling"            % karateVersion  % "test",
  "com.intuit.karate"     % "karate-apache"             % karateVersion  % "test"
)
```

### テスト作成：テストアサーション定義

```js:karate-config.js
function() {
  var config = {
      urlBase: 'http://localhost:9080/',
  };
  karate.configure('connectTimeout', 5000);
  karate.configure('readTimeout', 5000);
  return config;
}
```

PC性能の問題で負荷がかかり切らないので、今回はいくつかのパターンを停止しておく。

```Gherkin:karatesample.feature
Feature: Karate sample

  Background:
    * url urlBase

  Scenario Outline: get
    Given path 'projects'
    And header <name> = <values>
    And form field clientId = <clientId>
    When method <method>
    * print <exps>
    Then status <status>
    And match $ == '#[1]'
    And match <expression>
    Examples:
      | clientId | name   | values             | method | exps     | status | expression                                 |
      | '1'      | Accept | 'application/json' | get    | response | 200    | $[0] contains {"projectName": "プロジェクト００１"} |
      | '2'      | Accept | 'application/json' | get    | response | 200    | $[0] contains {"projectName": "プロジェクト００２"} |
      #| '3'      | Accept | 'application/json' | get    | response | 200    | $[0] contains {"projectName": "プロジェクト００３"} |
      #| '4'      | Accept | 'application/json' | get    | response | 200    | $[0] contains {"projectName": "プロジェクト００４"} |
      #| '5'      | Accept | 'application/json' | get    | response | 200    | $[0] contains {"projectName": "プロジェクト００５"} |
      #| '6'      | Accept | 'application/json' | get    | response | 200    | $[0] contains {"projectName": "プロジェクト００６"} |
      #| '7'      | Accept | 'application/json' | get    | response | 200    | $[0] contains {"projectName": "プロジェクト００７"} |
      #| '8'      | Accept | 'application/json' | get    | response | 200    | $[0] contains {"projectName": "プロジェクト００８"} |
      #| '9'      | Accept | 'application/json' | get    | response | 200    | $[0] contains {"projectName": "プロジェクト００９"} |
      #| '10'     | Accept | 'application/json' | get    | response | 200    | $[0] contains {"projectName": "プロジェクト０１０"} |

```

### テスト作成：負荷テスト設定定義

scalaファイルで記載すべきなのは負荷に関する定義のみ。

https://gatling.io/docs/current/general/simulation_setup/

TPSはfeatureのパターン×レートの数となるので注意
今回は2シナリオ×5=10TPSを目標とする。

```scala:KarateSimulation.scala
import com.intuit.karate.gatling.KarateProtocol
import com.intuit.karate.gatling.PreDef._
import com.intuit.karate.http.HttpRequestBuilder
import io.gatling.core.Predef._
import io.gatling.core.structure.ScenarioBuilder

import scala.concurrent.duration._

class KarateSimulation extends Simulation {

  // 開始時のレート
  val startRate: Double = 0
  // 最大負荷のレート(TPSはfeatureのパターン×ユーザの数となるので注意)
  val maxRate: Double = 5
  // 負荷を段階的に高める時間
  val rampUpTime: FiniteDuration = 30.seconds
  // 最大負荷のテスト時間(TPSはfeatureのパターン×ユーザの数となるので注意)
  val stableTime: FiniteDuration = 90.seconds

  val protocol: KarateProtocol = karateProtocol()
  protocol.nameResolver = (req: HttpRequestBuilder, _) => req.getUrlAndPath

  val search: ScenarioBuilder =
    scenario("get").exec(karateFeature("classpath:nablarch.feature"))
  setUp(
    search
      .inject(
        rampUsersPerSec(startRate) to maxRate during rampUpTime,
        constantUsersPerSec(maxRate) during stableTime
      )
      .protocols(protocol)
  )
}

```

## テスト実行：事前準備
テストさせる対象は、下記とする。
https://github.com/nablarch/nablarch-example-rest
先に、mvn waitt:run-headlessを実行しておくこと。

## テスト実行
コマンドラインで下記のコマンドを実行する。
IDEA(sbt shell)で実行する場合、sbt は除いて入力する

```
sbt test:compile
sbt gatling:test
```

## テスト結果

およそ30+90=120秒間、10TPSの負荷がかかった。エラーはなかった。
※request count.*OK=.*KO=.*　で結果検索できる

```
================================================================================
---- Global Information --------------------------------------------------------
> request count                                       1050 (OK=1050   KO=0     )
> min response time                                     18 (OK=18     KO=-     )
> max response time                                   2433 (OK=2433   KO=-     )
> mean response time                                   151 (OK=151    KO=-     )
> std deviation                                        297 (OK=297    KO=-     )
> response time 50th percentile                         40 (OK=40     KO=-     )
> response time 75th percentile                         77 (OK=77     KO=-     )
> response time 95th percentile                        703 (OK=703    KO=-     )
> response time 99th percentile                       1649 (OK=1649   KO=-     )
> mean requests/sec                                  8.824 (OK=8.824  KO=-     )
---- Response Time Distribution ------------------------------------------------
> t < 800 ms                                          1011 ( 96%)
> 800 ms < t < 1200 ms                                  20 (  2%)
> t > 1200 ms                                           19 (  2%)
> failed                                                 0 (  0%)
================================================================================

Reports generated in 1s.
Please open the following file: C:\Users\aruki\Desktop\dev\karate-gatling-sbt\target\gatling\karatesimulation-20210228145214326\index.html
[info] Simulation KarateSimulation successful.
[info] Simulation(s) execution ended.
[success] Total time: 126 s (02:06), completed 2021/02/28 23:54:17
[IJ]
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/161516/4956887b-c894-23e1-320f-f78900be8bb5.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/161516/3d8c3778-e2af-a6d4-7919-63ae4463d44c.png)