Gatling の主要概念まとめ

## はじめに

Gatling の主要概念についての簡単な整理です。

これまで雰囲気でスクリプトを書いてしまっていたので、きちんと理解するために公式ドキュメントを読んで、自分なりの理解を言葉と図でまとめてみました。

### 記事に書かないこと

Gatling の使い方

### 記事の構成

次の２部構成になっています。

1. 言葉による3行まとめ
2. 図によるまとめ

## 3行まとめ

公式ドキュメントの Glossary の項目について、それぞれ3行でまとめています。

https://docs.gatling.io/reference/glossary/

### Gatling の特徴

#### Domain-Specific Language (DSL) / 負荷試験を “定義” するための DSL

- Gatling のテストスクリプトは DSL で記述する
- DSL は試験内容を “定義” するだけ
- setUp メソッドに渡さない限り、リクエストが投げられることはない

#### Immutability / あらゆるものがイミュータブル[^immutable]

- 提供されるクラスは基本的にすべてイミュータブル
- どのメソッドもインスタンスの状態は変更しない
- 状態が変更された新しいインスタンスを返すだけ

### テストスクリプトにおける重要な概念

#### Virtual User / 仮想ユーザー

- Gatling では “仮想ユーザー” による操作を模したスクリプトを記述する
- 仮想ユーザーはそれぞれ独立したデータを保持できる
- Cookie やブラウザキャッシュなど、実際のブラウザ操作によるアクセスをある程度再現できる

#### Scenario / シナリオ

- 仮想ユーザーによる操作を DSL で記述したもの
- DSL でループや条件分岐などの制御が可能
- DSL で用意されていない処理もコードで記述可能

#### Injection Profile / 仮想ユーザーの “注入”

- シナリオを実行する仮想ユーザーのアクセス頻度やユーザー数の指定
- Open Model と Closed Model をサポート
- Open Model は秒間ユーザー数を指定、Closed Model は同時ユーザー数を指定

#### Simulation / システム負荷のシミュレーション

- テスト実行の単位
- 実際のシステム負荷のシミュレーション
- 複数のシナリオをまとめて並列実行、または順次実行可能

### シナリオ作成のための機能

#### Session / 仮想ユーザーのデータストア

- 仮想ユーザーごとに独立したデータストア
- Feeder や HTTPレスポンスなどから値を保存可能
- 保存された値は EL 式で URL やリクエストなどに埋め込み可能

#### Feeders / 仮想ユーザーのデータソース

- すべての仮想ユーザーで共有するデータソース
- CSV や JSON 形式のファイルを元データとして利用可能
- List などを元にコード上で定義することも可能

#### Checks / エラー判定とデータ保存

- リクエストの成功(OK)、失敗(KO)の判定
- CSSセレクタ や JSONPath、XPath、JMESPath などで抽出した値で検証可能
- 抽出した値を Session に保存することも可能

### その他

#### Assertions

- シミュレーション全体の成功、失敗の判定
- 「全リクエスト」を対象に「レスポンスタイム」の「平均」が「100ms以下」というように、スコープ、測定値、統計量、合格基準、の４つを指定
- 結果は JSON と XML ファイル(JUnit 互換) の２種類で出力


#### Reports

- テスト結果のレポートが static な HTML で出力される
- 統計値は `.js` や `.json` ファイルで書き出される
- 統計値の区分（Response Time の percentile など）は gatling.conf で変更可能

#### Durations

- Gatling の duration の単位は「秒」
- これは３行書くことがない
- 公式ドキュメントでもここだけ粒度がおかしいと思います…


****

## 図によるまとめ

### 主要概念

![Conceptsの概要図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/771456/c7bf5b6b-7c97-dde6-cbdf-597be4976b81.png)

- シナリオを元に仮想ユーザーがテストを実行する
- `feed` メソッドでは Feeder から値を取得して Session に保存する
- EL式による変数展開では Session から値が取得される
- HTTP レスポンスの中身を抽出して検証したり Session に保存できる

### Injection

![Injection.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/771456/fa2a7f9f-4b8c-30d4-56a5-283dcc82ec9a.png)

- 仮想ユーザーを投入する頻度、タイミングなどの設定
- 投入された仮想ユーザーは、同じシナリオを参照する

### シナリオの可変部分

同じシナリオを参照する仮想ユーザーの挙動を変える手段は大きく２つ。どちらも Session に値を保存、更新することで挙動を変えます。

#### Feeder

![Feederの概要図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/771456/978dfff6-bef0-bd2c-6e02-9bab6ffd81b5.png)


- シナリオ実行中、feed メソッドのところに到達したら Feeder から値を取得
- 取得した値は Session に保存される
- 共通のキューから値を取得するので、各仮想ユーザーが feed メソッドを呼んだタイミングによって取得する値が異なる

```kotlin:例
.feed(csv("userid.csv")) // ここで１行分のデータが Session に追加
.exec(http("sample").get("/users/#{userId}")) // それをEL式で展開するようなシナリオにしておく
```

#### exec メソッド

![lambda式を使った場合の概要図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/771456/fb621b0f-79b8-d985-2177-8201adf63aed.png)

- exec メソッドの引数のラムダ式は `(Session) -> Session` 型
- 現在の Session を受け取り、更新した Session を返す

```kotlin:例
.exec { session -> session.set("userId", Random.nextInt()) } // Session に値を追加
.exec(http("sample").get("/users/#{userId}")) // それをEL式で展開するようなシナリオにしておく
```

[^immutable]: 公式ドキュメントでは "Most Gatling APIs" となっているので「あらゆるもの」は言い過ぎかもしれません。