Contents
Outline
Recently, the number of test codes has increased in our company, and the Action that checks the code in GitHub Actions has taken a long time. In this blog post, I would like to share how I improved the performance of the Action that runs Jest.
Problem
Currently my team uses the following Action to check 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
When a PR is created, the Prettier, CSpell, ESLint, Stylelint, Test, Build, and Build SCSS are executed. This Action takes about 25 minutes like the following.

Performance Improvement
GitHub Actions that take 25 minutes every time a PR is created are very inefficient. In this blog post, I will introduce how to improve the performance of the Action that runs Jest.
Cache Dependencies
The first thing I did to improve the performance of the Action that runs Jest was to cache the Dependencies installed by running yarn install. This reduces the time it takes to reinstall Dependencies.
This part can be used in other Actions, so I created a separate Composite Action. If you want to know more about Composite Action, please refer to the following link.
I used actions/cache to cache the Dependencies.
actions/cacheofficial document: https://github.com/actions/cache
The Composite Action that caches Dependencies using actions/cache is as follows.
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
Our team uses Yarn version 3.7.0. So I added corepack enable and cached the cache folder of Yarn 3.7.0 and node_modules together.
Also, since the project is a monorepo, I cached the node_modules in the sub projects using **/node_modules.
If you are not using a monorepo and are not using Yarn 3.7.0, please refer to the official document and make appropriate settings.
- Official document: https://github.com/actions/cache?tab=readme-ov-file#implementation-examples
After caching the Dependencies, the performance was slightly improved.
- Before: 1m 25s

- After: 9s

Separate Actions
I executed Prettier, CSpell, ESLint, Stylelint, Test, Build, and Build SCSS in one Action. Among them, the Test part took the longest time.
So I separated the Actions so that other Actions can be performed while Test is running.
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
Since we are using a monorepo, GitHub Actions like this exist for each service, and I used an if statement to run only the service.
Before separating the Actions, this Action took about 25 minutes.

After separating the Actions, this Action took about 13 minutes.
Jest bail option
By using the bail option of Jest, you can stop the test if one of the tests fails.
By adding this option, you can stop all tests immediately when a test fails, so you can save time.
To set the bail option of Jest, open the package.json file that contains the command to run jest and modify it as follows.
{
...
"scripts": {
...
"test:ci": "jest --ci --bail"
},
...
}
Jest shard option
By using the shard option of Jest, you can run tests in parallel. To run tests in parallel using the shard option, open the Action that runs Jest and modify it as follows.
...
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
This project uses Turborepo to manage the monorepo.
Turborepoofficial document: https://turborepo.com/
The Action that runs yarn test:ci:service_1 in the Action is executed by running the turbo test:ci --parallel --filter=service_1 command. So I used -- to pass the option to use the --shard option of Jest. So, to pass the shard option, I used --.
Before using the shard option, the test took about 13 minutes.
After using the shard option, the test took about 2~3 minutes.

Completed
Done! We’ve seen how to improve the performance of the Action that runs Jest. Before the performance improvement, it took about 25 minutes, but after the performance improvement, it took about 2~3 minutes.
If you have a similar problem, I hope you can improve the performance of the Action that runs Jest by caching Dependencies, separating Actions, and using the shard option.
Was my blog helpful? Please leave a comment at the bottom. it will be a great help to me!
App promotion
Deku.Deku created the applications with Flutter.If you have interested, please try to download them for free.



