- 本記事は 虎の穴ラボ Advent Calendar 2023 16日目の記事です。(予約投稿)
- 前回は godanさんの「Akihabara入門 at akihabara」でした。
- 次回は 中村さんの「GPT-4Vを用いた業務活用例」です。 ご期待ください!
こんにちわ、虎の穴ラボのH.Y.です。 今日は、今のプロジェクトで自分が行ったCypressの並列実行について話したいと思います。
Cypressとは
Cypressは直感的で使いやすいエンドツーエンド(E2E)テストフレームワークです。 テストの記述から実行までの流れが簡素でわかりやすく、デバッグも容易です。 ローカル上でリアルタイムにテスト結果閲覧や操作の自動待機機能により、テスト実行の手間が大幅に削減されます。 また複数ブラウザ対応やネットワークリクエストの制御も可能で、テストの範囲と詳細さを自在に調整できます。
構成
フロントエンド:Next.js
- ビルド:npm
- テスト:Cypress
バックエンド:Kotlin
- ビルド:gradle
- 自動テスト:JUnit
GitHub Actionsでプルリクが作成されるたびにビルド、自動テストを実行という構成です。 mainブランチにマージするには自動テストに通らなければマージできないという仕組みです。
問題点
今回の主題となるフロントエンドのCypressによるテストですが、テストが増えるたびに実行時間が伸びます。 当初3分だったのが今では13分とかなり伸びました。 また、mainブランチに新しいコミットが入るたびに、その変更を取り入れてCypressのテストを再度実行する必要があるので、 かなり時間が掛かってしまいます。
検討
Cypressの時間短縮には、テストの並列実行が考えられます。 しかし並列実行には、CypressのTeamプラン(67USD/月)以上に入る必要があり、加えて1000テストごとに6USD必要なので、なかなかのコストがかかります。
これをもっと安くできないかと考えた結果
GitHub Actionsのmatrixを利用することで実質並列実行にならないかと考えました。 matrixの本来の使い方はnode.jsの異なるバージョンとかOSを同時にテストすることに使うのですが、 これを使ってテストファイルごとにCypressを実行し、時短するという方法です。
単純計算だとテストファイルが33ファイルあるので33並列で実行できるということで速くなりそうです。
実装
まず、プロジェクトの中にあるCypressのテストコード(.cy.tsx)のファイル一覧を取得し、 そのファイル数だけmatrixの部分で同時実行させます。
# テストコードの一覧を取得する prepare_matrix: needs: build runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 with: ref: ${{ github.head_ref }} - name: Get test file list id: gettest run: | echo "::set-output name=list::$(find ./ -name '*.cy.tsx' -printf '%P\n' | jq -R -s -c 'split("\n")[:-1]' | jq -c '[.[] | {file: .}]')" shell: bash outputs: matrix: ${{ steps.gettest.outputs.list }}
ビルド部分です。 TypeScriptのためビルドしないと、Cypressが実行できないためビルドします。 ただ、後続処理にビルドしたファイルを渡す必要があるため、 ビルドしたファイルはactions/cache@v3でキャッシュします。
ここで、キャッシュしないとあんまり時短ならないのと、 ファイル数*ビルド時間(今回の場合だと2分)のGitHub Actionsの稼働料金をが取られるので、キャッシュした方が良いです。 keyにgithub.run_idを指定している理由は、この実行時のみのキャッシュにするためです。
build: runs-on: ubuntu-latest defaults: run: working-directory: ./frontend/creator steps: - name: Checkout uses: actions/checkout@v3 with: ref: ${{ github.head_ref }} - name: Cache node modules and Cypress binary uses: actions/cache@v3 with: path: | **/node_modules ~/.cache/Cypress key: ${{ runner.os }}-modules-${{ github.run_id }} - name: Install run: | npm install - name: Lint run: | npm run lint - name: Cache build output uses: actions/cache@v3 with: path: ./.next key: ${{ runner.os }}-nextjsbuild-${{ github.run_id }} - name: Build run: | npm run build npm run generate:client
ここが並列実行部分です。 テストファイルごとにCypressを実行しています。 buildで作ったキャッシュを利用してCypressを実行します。 matrix.fileで指定されたファイルのテストを実行しています。
cypress-run: runs-on: ubuntu-latest defaults: run: working-directory: ./frontend/creator needs: prepare_matrix strategy: fail-fast: false matrix: include: ${{fromJson(needs.prepare_matrix.outputs.matrix)}} steps: - name: Checkout uses: actions/checkout@v3 with: ref: ${{ github.head_ref }} - name: Cache node modules and Cypress binary uses: actions/cache@v3 with: path: | **/node_modules ~/.cache/Cypress key: ${{ runner.os }}-modules-${{ github.run_id }} - name: Install run: | npm install - name: Cache build output uses: actions/cache@v3 with: path: ./.next key: ${{ runner.os }}-nextjsbuild-${{ github.run_id }} - name: Run run: npm run test:component:headless -- --spec ../../${{ matrix.file }}
検証
改善前が11分
改善後が5分43秒
という感じで、5分ほどテストが短縮されました。 しかし、GitHub Actionsのトータル実行時間が13⇨53分と40分増加しました。
GitHub Actionsの課金が2CPUで0.008USD/分のため 0.008USD/分*40分 = 0.32USD(48円)増加でした。
1回200テストぐらいなので、Cypressの場合1.2USDです。 従量課金分でもこちらの方が安い感じです。
まとめ
Cypressでの並列実行ではなく、GitHub Actionsを使って、フロントエンドのテストの並列実行をしてみました。 結果的に、安く、速くなりました。
わかっている問題点として
- ファイルごとなので、1ファイルに大量のテストがある場合、一番遅いファイルのテストに影響されるので注意が必要
- GitHub Actionsの仕様で256並列以上では起動できないので注意が必要 です。
注意点を気をつければ、安く自動テストの時短になると思います。