概要
最近、会社でテストコードが増えてきたため、GitHub ActionsでコードをチェックするActionに時間がかかるようになりました。この問題を解決するために、Jestを実行するActionのパフォーマンスを改善した内容を共有したいと思います。
問題点
現在、次のようなActionを使用してPRをチェックしています。
jobs:
check-code:
name: Check Code
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
uses: ./.github/actions/install_dependencies
- name: Prettier
run: yarn format
- name: CSpell
run: yarn cspell
- name: ESLint
run: yarn lint
- name: Stylelint
run: yarn stylelint
- name: Test
run: |
yarn test:ci
yarn test:storybook
- name: Build
run: |
yarn build
- name: Build SCSS
run: |
yarn typegen:scss
# Get the changed files
CHANGED_FILES=$(git diff --name-only HEAD)
# Check if there are changes in the generated files
if [ -n "$CHANGED_FILES" ]; then
echo "Error: There are changes in the following files: $CHANGED_FILES"
exit 1
fi
PRが作成されるたびにPrettier、CSpell、ESLint、Stylelint、Test、Build、Build SCSSが実行されています。このActionは約25分かかります。

性能改善
PRを生成するたびに25分かかるのは非常に非効率的です。このブログポストでは、Jestを実行するActionのパフォーマンスを改善する方法について説明します。
Dependencies Cache
一番最初に行ったのは、yarn installでインストールされるDependenciesをキャッシュすることです。これにより、Dependenciesを再インストールする時間を短縮できます。
この部分は他のActionsでも使用できるため、Composite Actionとして作成しました。Composite Actionについては、次のリンクを参照してください。
キャッシュはactions/cacheを使って行いました。
actions/cacheの公式ドキュメント: https://github.com/actions/cache
actions/cacheを使ってDependenciesをキャッシュするComposite Actionは次のようになります。
name: 'Install Dependencies'
description: 'Install Dependencies'
runs:
using: 'composite'
steps:
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: 20.3.0
- name: Install dependencies
- name: Enable Yarn 3.7.0
shell: bash
run: corepack enable
- name: Get yarn cache directory path
shell: bash
id: yarn-cache-dir-path
run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4
id: yarn-cache
with:
path: |
node_modules
**/node_modules
${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install dependencies
if: steps.yarn-cache.outputs.cache-hit != 'true'
shell: bash
run: yarn install --frozen-lockfile
私たちのチームはYarnの3.7.0バージョンを使っています。そのため、corepack enableを追加し、Yarn 3.7.0のキャッシュフォルダを取得してnode_modulesと一緒にキャッシュしました。
また、プロジェクトがモノレポであるため、**/node_modulesを使ってサブプロジェクトのnode_modulesもキャッシュしました。
もし、皆さんのプロジェクトがモノレポではなく、Yarn 3.7.0も使っていない場合は、公式ドキュメントを参照して適切な設定を行ってください。
この変更により、パフォーマンスが少し向上しました。
- Before: 1m 25s

- After: 9s

Actions分離
Prettier、CSpell、ESLint、Stylelint、Test、Build、Build SCSSをすべて1つのActionで実行していました。その中で最も時間がかかったのがTestの実行でした。
なので、Testが実行される間に他のActionsを実行できるようにActionsを分離しました。
jobs:
cspell:
name: CSpell
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
uses: ./.github/actions/install_dependencies
- name: CSpell
run: yarn cspell
remark:
if: contains(github.head_ref, 'service_1') || contains(github.head_ref, 'npm_and_yarn') || contains(github.head_ref, 'github_actions') || contains(github.head_ref, 'components') || contains(github.head_ref, 'config') || contains(github.head_ref, 'common')
name: Remark-lint
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
uses: ./.github/actions/install_dependencies
- name: Remark-lint
run: yarn remark:service_1
eslint:
if: contains(github.head_ref, 'service_1') || contains(github.head_ref, 'npm_and_yarn') || contains(github.head_ref, 'github_actions') || contains(github.head_ref, 'components') || contains(github.head_ref, 'config') || contains(github.head_ref, 'common')
name: ESLint
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
uses: ./.github/actions/install_dependencies
- name: ESLint
run: yarn lint:service_1
stylelint:
if: contains(github.head_ref, 'service_1') || contains(github.head_ref, 'npm_and_yarn') || contains(github.head_ref, 'github_actions') || contains(github.head_ref, 'components') || contains(github.head_ref, 'config') || contains(github.head_ref, 'common')
name: Stylelint
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
uses: ./.github/actions/install_dependencies
- name: Stylelint
run: yarn stylelint:service_1
build:
if: contains(github.head_ref, 'service_1') || contains(github.head_ref, 'npm_and_yarn') || contains(github.head_ref, 'github_actions') || contains(github.head_ref, 'components') || contains(github.head_ref, 'config') || contains(github.head_ref, 'common')
name: Build
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
uses: ./.github/actions/install_dependencies
- name: Build
run: yarn build:service_1
test-service_1:
if: contains(github.head_ref, 'service_1') || contains(github.head_ref, 'npm_and_yarn') || contains(github.head_ref, 'github_actions') || contains(github.head_ref, 'components') || contains(github.head_ref, 'config') || contains(github.head_ref, 'common')
name: Test service_1
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
uses: ./.github/actions/install_dependencies
- name: Test
run: yarn test:ci:service_1
モノレポを使っているため、このようなGitHub Actionsはサービスごとに存在し、if文を使ってそのサービスだけが実行されるようにしています。
分離する前には、このActionは約25分かかりました。

分離した後、このActionは約13分程度で性能が改善されました。
Jestのbail
Jestのbailオプションを使うと、テスト中に1つでもテストが失敗した場合にテストを中止するように設定できます。
このオプションを追加することで、テストが失敗した場合にすぐにすべてのテストを実行せずに中止することができ、時間を短縮できます。
Jestのbailオプションを設定するために、jestを実行するコマンドが含まれるpackage.jsonファイルを開いて、次のように変更します。
{
...
"scripts": {
...
"test:ci": "jest --ci --bail"
},
...
}
Jestのshardオプション
Jestのshardオプションを使うと、テストを並列で実行できます。shardを使ってテストを並列で実行するために、Jestを実行するActionを開いて次のように変更します。
...
test-service_1:
if: contains(github.head_ref, 'service_1') || contains(github.head_ref, 'npm_and_yarn') || contains(github.head_ref, 'github_actions') || contains(github.head_ref, 'components') || contains(github.head_ref, 'config') || contains(github.head_ref, 'common')
name: Test service_1
runs-on: ubuntu-latest
strategy:
matrix:
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
uses: ./.github/actions/install_dependencies
- name: Test
run: yarn test:ci:service_1 -- --shard=$/10
このプロジェクトでは、モノレポを管理するためにTurborepoを使っています。
Turborepo公式ドキュメント: https://turborepo.com/
このActionで実行するyarn test:ci:service_1はturbo test:ci --parallel --filter=service_1コマンドを実行します。そのため、Jestの--shardオプションを渡すために--を使ってオプションを渡しました。
shardオプションを追加する前のテストは約13分かかりました。
shardオプションを追加した後、テストは約2~3分かかりました。

完了
このブログポストでは、Jestを実行するActionのパフォーマンスを改善する方法について説明しました。パフォーマンス改善前には約25分かかりましたが、パフォーマンス改善後には約2~3分かかるようになりました。
皆さんもCache、Actions分離、shardオプションを使ってJestを実行するActionのパフォーマンスを改善してみてください。
私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!
アプリ広報
Dekuが開発したアプリを使ってみてください。Dekuが開発したアプリはFlutterで開発されています。興味がある方はアプリをダウンロードしてアプリを使ってくれると本当に助かります。






