【runn】APIシナリオテストにおけるrunnの魅力

:::note info
この記事は、[ラクス Advent Calendar 2024](https://qiita.com/advent-calendar/2024/rakus)の6日目の記事です。
昨日は、[@hairihou](https://note.com/hairihou)さんの[忘れる技術](https://note.com/hairihou/n/n15f50f95abb1)の記事でした。
明日は、[@TKDS](https://qiita.com/TKDS)さんの[Goのhttpサーバー入門](https://qiita.com/TKDS/items/f65c4eb2941e80a31f1c)の記事です。
:::

## はじめに
今回の記事では、APIシナリオテスティングツールとして「runn」を調査利用した過程で感じた魅力について共有します。

## runnとは
[公式リポジトリ](https://github.com/k1LoW/runn)

> runn ( means "Run N". is pronounced /rʌ́n én/. ) is a package/tool for running operations following a scenario.

runnは、YAMLで書かれたシナリオ（APIのシナリオテスト）を自動化できるツールです。


## 前提
- FEとBEが分かれているアプリケーションを開発している
- テストピラミッドの、UnitテストとE2E（UI込み）テストはしっかりあるが中間のインテグレーションテスト（テストサイズの文脈でいうMediumサイズ）がほぼ無い状態
- この記事ではインテグレーションテストの範囲を、バックエンドのREST APIサーバー＋DBとする
- プロダクトの中で一番重要なハッピーパスは、手動テストで確認を行なっている
- ここに自動インテグレーションテストを用意しておけば、今後いくつかの場面で恩恵を得られるのでは？と考えていた
- インテグレーションのテスト実行方法は大きく2種類を想定
    - テストコードを用いたテスト
        - モックを利用せず、Controller→DBまでの通しのテスト
    - 外部からリクエストを叩くテスト **（今回はこちらでCIから叩く想定）**
        - 任意環境で起動したアプリケーションに対するテスト

## runnを選択したきっかけ
外部からリクエストを叩くツールを探していた中で、以前から「[tbls](https://github.com/k1LoW/tbls)」や「[octocov](https://github.com/k1LoW/octocov)」を開発している[k1LoWさん](https://github.com/k1LoW)の「runn」が気になっていたので、これを機に触ってみました。


## runnの魅力

### 1. シングルバイナリ
まずは圧倒的にこれです。
CIでの実行はDockerコンテナを用いる場合が多いかと思いますが、runnはシングルバイナリでの配布も行っています。
このシングルバイナリのインストールと初回の動作確認までの体験が、本当に素晴らしかったです。

利用者側としてはツールを試す際、とりあえず正常に最小限に動くことを試して少しづつ使いこなしていくかと思いますが、このハードルが異常に低かったです。
CIの前に動きを確認したい場合でも、ローカルでサクッと試せます。
GitHubの場合は、[Actions](https://github.com/marketplace/actions/run-runn)も公開されていますのでそちらを利用できます。

```yaml:.gitlab-ci.yml
integration-test:
  image: almalinux:8.7
  stage: integration-test
  
  script:
    - rpm --import https://repo.almalinux.org/almalinux/RPM-GPG-KEY-AlmaLinux
    - RUNN_VERSION=X.XXX.X
    - yum install -y https://github.com/k1LoW/runn/releases/download/v$RUNN_VERSION/runn_$RUNN_VERSION-1_amd64.rpm
    - runn --version
```

```yaml:ci.yml
name: Runn Test

on: 
  push

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run version check
        uses: k2tzumi/runn-action@latest
        with:
          path_pattern: ""
          command: "--version"
```

### 2. シンプルで高い可読性
APIのシナリオテストを実行するためのYAML記述フォーマットは、OpenAPIの仕様に似た構造を持っており、直感的に理解しやすいものとなっています。
また、GitHub Actionsのフォーマットとの類似性も個人的には感じられ、既存の知識が活かせる点も魅力です。

基本的に「1YAMLファイル＝1ユースケースのシナリオテスト」という考え方により、APIの実行順序や必要なエンドポイントが一目で把握できます。
これはテストコードとしてだけでなく、ドキュメントとしても機能するのかなと感じました。
また他にもシナリオに必要な事前準備を別ファイルとして分割したり、headerの再利用なども兼ね備えているので、ごちゃごちゃとした記述になりにくいのも特徴です。


### シナリオイメージ
```yaml:scenario.yml
desc: シナリオリグレッションテスト実施
needs:
  # 事前準備用のシナリオを先に実行
  # ログインなどの処理をゴニョゴニョ行い、必要な値を後続の処理に引き継ぐ
  prepare: ./prepare.yml
runners:
  req:
    endpoint: http://example.com
    # headersの再利用のための設定
    headers: &defaultHeaders
      Accept: hoge
      content-type: hoge
      User-Agent: hoge
steps:
  checkStatus:
    desc: "ステータスを確認"
    req:
      /{{ needs.prepare.Code }}/api/v1/status:
        get:
          # ここでheadersの再利用
          headers: *defaultHeaders
    test:
      current.res.status == 200
      && current.res.body['isSuccess'] == true
  createData:
    desc: "データの新規作成"
    req:
      # 事前準備で取得した値の再利用
      /{{ needs.prepare.Code }}/api/v1/data:
        post:
          headers: *defaultHeaders
          body:
            application/json:
              "name": "hoge"
              "age": 20
    test:
      current.res.status == 200
```


### 3. SQLクエリも叩ける
こちらは意外でしたが、SQLクエリも叩けます。
今回は検証していませんが、gRPCやChrome DevTools Protocol、ローカル/SSHの任意コマンドの実行も可能だそうです。
テスト前後のDBの冪等性担保や事前データ準備に、Testcontainersなどを使えないのでこうしたプロトコルに対応してるのは凄くありがたかったです。

```yaml:sql.yml（イメージ）
desc: ログイン後に作成したデータを一覧取得
runners:
    db: postgres://dbuser:dbpass@hostname:5432/dbname
steps:
    - desc: DBからデータ一覧取得
      db:
          query: SELECT * FROM hoges;
```

### 4. Goのテストヘルパーとしての活用
検証は行っていませんが、社内のtimesでrunnを布教？していたところGoで開発をしている同僚にGoのTest Helperとして使える事実が刺さったようでした。
`go test`コマンドで単体テストとインテグレーションテストを簡単に実行できるのは、確かにメリットの一つなのかもと思いました。


## まとめ
実は現時点では本格運用には至っていませんが、この記事を読んでAPIシナリオテスティングツールを検討されている方々に少しでも魅力が伝わり候補に入れていただけると、嬉しいです。
手を動かしてみたい方は、以下のZenn Booksが非常に有用なので是非ご覧いただけると幸いです。
[runn クックブック](https://zenn.dev/k1low/books/runn-cookbook)
[runnチュートリアル](https://zenn.dev/katzumi/books/runn-tutorial)

またk1LoWさんのSpeakerDeckも併用してご確認いただけると、ツールの使い方に限らずテストの考え方や戦略なども学べますのでおすすめです。
[CI/CDがあたりまえの今の時代にAPIテスティングツールに求められていること / CI/CD Test Night #7 - Speaker Deck](https://speakerdeck.com/k1low/cd-test-night-number-7)
[Win Testing Trophy Easily / テスティングトロフィーを獲得する / PHPerKaigi 2023 - Speaker Deck](https://speakerdeck.com/k1low/phperkaigi-2023)
[runnによるAPIのシナリオテストの導入と自動化 / stac2022 - Speaker Deck](https://speakerdeck.com/k1low/stac2022)


## さいごに
こちらで以上となります。
弊社のエンジニアがこれからも更に投稿していきますので、ぜひ他の記事もご覧になって引き続き[ラクス Advent Calendar 2024](https://qiita.com/advent-calendar/2024/rakus)をお楽しみください！