技術(tech)

k6 – CIでスモークテストを実行してシナリオの品質を担保する

はじめに

CIで継続的にテストを実施することは、プロダクト開発において非常に重要です。

負荷試験においても上記は例外ではなく、久しぶりに負荷スクリプトを動かしてみたら、全く動かなかった。という事態は避けたいものです。

今回は、CI上でモック用のAPIサーバに対してk6の負荷スクリプトを実行し、継続的な自動テストを実現します。

なお、本記事は以下の記事でモック用のAPIサーバを立てていることが前提となっています。
必要に応じて、以下の記事もご覧くださいませ。

k6 - シナリオをテストするためにモックAPIサーバを構築するはじめに k6で負荷試験用のスクリプトを作成する際に、負荷シナリオに対するテストをどう書けばいいものなのか、疑問が湧きます。 テストの方...

実現すること

こちらのPRで実行されているような、mockTestForSampleApp CIをGitHubActionsで定義し、自動で実行します。

結論だけ知りたい方は、以下のPRをご覧くださいませ。
https://github.com/gonkunkun/k6-template/pull/3

CIを用意する

前提知識

以下のサンプルリポジトリをベースに説明いたします。
https://github.com/gonkunkun/k6-template

CI上の登場人物は以下の通りです。

k6

  • 拡張機能を扱うために、xk6を利用しています
  • 実行コマンドはgoでbuildして生成しています

Redis

Mock API Server

  • k6から実際のAPIサーバをCallするのではなく、代わりにMock用のAPI ServerをCallしています

構築

CI用のenvファイル(.env.c)を準備する。

AMOUNT_OF_INDEX_SIZE_FOR_TEST_DATA=10000
DEBUG=true
ENV=local
SAMPLE_PRODUCT_ENDPOINT=http://localhost:3005
REDIS_ENDPOINT=redis://localhost:6379

.github/workflows/mockTest.ymlを作成する。

name: mockTest

on: [pull_request]

jobs:
  mockTestForSampleApp:
    runs-on: ubuntu-latest
    services:
      redis:
        image: redis
        ports:
          - 6379:6379

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 21
          cache: 'npm'

      - name: Cache npm directory
        uses: actions/cache@v4
        with:
          path: ~/.npm
          key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-npm-
      - name: Install npm dependencies for k6
        run: npm install

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.22'
          cache-dependency-path: '**/*.sum'
      - name: Install xk6
        run: go install go.k6.io/xk6/cmd/xk6@latest

      - name: Cache k6 binary
        uses: actions/cache@v4
        id: cache-k6-binary
        with:
          path: k6
          key: ${{ runner.os }}-k6-${{ hashFiles('**/k6.*') }}
          restore-keys: |
            ${{ runner.os }}-k6-
      - name: Build k6 with plugins
        if: steps.cache-k6-binary.outputs.cache-hit != 'true'
        run: |
          xk6 build \
          --with github.com/LeonAdato/xk6-output-statsd@latest \
          --with github.com/grafana/xk6-dashboard@latest \
          --with github.com/szkiba/xk6-enhanced@latest \
          --with github.com/szkiba/xk6-dotenv@latest

      - name: Set up environment variables
        run: cp .env.ci .env
      - name: Start mock endpoint
        run: |
          cd mock
          npm install
          npx ts-node ./src/index.ts 3005 &

      - name: Cache k6 bundle files
        uses: actions/cache@v4
        id: cache-k6-bundle-files
        with:
          path: dist
          key: ${{ runner.os }}-dist-${{ hashFiles('dist/**') }}
          restore-keys: |
            ${{ runner.os }}-dist-
      - name: Bundle k6 scripts
        if: steps.cache-k6-bundle-files.outputs.cache-hit != 'true'
        run: npm run bundle
      - name: Run k6 smoke test
        run: npm run smoke:sample-product

諸々の環境を準備したうえで、最後にスモークテスト用のシナリオを実行しています。

        run: npm run smoke:sample-product

後は、普通にPRを作成してみて、CIが動作することを確かめましょう。

CIの実行ログを見てみると、無事にk6が動いていることが確認出来ます。

なお、一部cacheが上手く効いていない箇所があります。
今後の課題ということでご勘弁くださいませ。

おわりに

CIによって、毎回k6のシナリオが壊れていないことを確認出来るようにりました。

本記事で利用していたリポジトリの説明は、以下の記事でしています。
興味がある方はこちらもご覧くださいませ。

k6 - 負荷試験を簡単に始められるプロジェクトテンプレートk6用のプロジェクトテンプレートを作成したので、紹介します。 いざ負荷試験のスクリプトを作り始めるとなった際に、色々と整備して初期構築をするのも大変です。今回は、試行錯誤しつつ導き出した自分なりの良さげな構成例を紹介します。 複数のシナリオと、そのシナリオを束ねるシナリオセットはどう管理する? 負荷試験対象の環境情報(local, stg, etc...)を簡単に切り替えられうようにしたい シナリオの設定値(VU、イテレーション数等)は簡単に渡せるようにしたい コードフォーマットはなるべく統一させたい 等...

負荷試験スクリプトのテストコードをどこまで書くべきなのか、これは議論が分かれるところだと思います。
今回の例では、k6が実行出来てシナリオが動き切るところまでは確認出来ますが、シナリオ内で叩かれるモジュール、APIの振る舞いの担保は出来ていません。

この辺、上手いやり方をご存じの方がいたら、こっそりご連絡ください…

ここまで読んでいただき、ありがとうございました。