CI/CD — Improve The Testing Time in Jest and GitHub Action with 90% Effectiveness

Didik Mulyadi
3 min readAug 25, 2022

--

Hi Everyone! I wanna share my experience regarding how I reduced 90% of the testing time by Jest in GitHub Action.

Stacks:
- Node Typescript
- Jest v17
- Supertest
- GitHub Action

Yesterday, Our team have an issue regarding the integration testing time which takes a long time in GitHub Action when running more than 1000 test cases. It takes 90–120 minutes but the result still failed because of the timeout.

Besides that, there are random errors that happened because of many test cases that running at the same time / at the same GitHub Job (I named the job is “running-test”).

I try to increase a jest.setTimeout(), but the result is still failed and the test running takes a more long time (> 120minutes) 😭.

Jest v18 Shard Parameters

Because that issue happened while running the test at the same time.
So I think “What if we run a test case as a part? if we have 1000 test cases, we can run per 200, so there are 5 parts”.

I found “shard” parameters in Jest v18.

As the result, I have 5 jobs:
1. “running-test-1”
2. “running-test-2”
3. “running-test-3”
4. “running-test-4”
5. “running-test-5”

Istanbul

the next question is “How do we calculate the complete test coverage?”.

So, I upload each test coverage from “running-test-1”, …, to “running-test-5”. In the end, I download those files and merge them with “NYC Istanbul”.

It just solves about successfully running a test, not about execution time.

GitHub Matrix and Cache

the next question is “How do we reduce the execution time?”.
So, I use GitHub Matrix to run 5 jobs in parallel.

Now the test execution time is around 5–7 minutes 🥳

Summary solution:
- Use Jest v18 “shard” parameters
- Use NYC Istanbul to merge each coverage into a single file
- Use Github Matrix to make the test run parallel
- Use Github caching to cache “node_modules” and other’s

jobs:
install-dependencies:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Cache dependencies
id: cache
uses: actions/cache@v3
with:
path: ./node_modules
key: ${{ runner.os }}-${{ env.MODULE_CACHE_NAME }}-${{ hashFiles('**/package-lock.json') }}
- name: Install dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: npm install
run-tests:
runs-on: ubuntu-latest
needs: [install-dependencies]
strategy:
matrix:
shard: [1, 2, 3, 4, 5]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Cache dependencies
id: cache-nextjob
uses: actions/cache@v3
with:
path: ./node_modules
key: ${{ runner.os }}-${{ env.MODULE_CACHE_NAME }}-${{ hashFiles('**/package-lock.json') }}
- name: Install dependencies
if: steps.cache-nextjob.outputs.cache-hit != 'true'
run: npm install
- name: Run Tests
run: npm run test -- --max-workers=2 --shard=${{ matrix.shard }}/${{ strategy.job-total }}
- name: Rename coverage to shard coverage
run: |
mv coverage/clover.xml coverage/clover-${{matrix.shard}}.xml
mv coverage/lcov.info coverage/lcov-${{matrix.shard}}.info
mv coverage/coverage-final.json coverage/coverage-${{matrix.shard}}.json
- uses: actions/upload-artifact@v3
with:
name: coverage-artifacts
path: coverage/
check-coverage:
runs-on: ubuntu-latest
needs: [run-tests]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/download-artifact@v3
with:
name: coverage-artifacts
path: coverage
- name: Process Coverage
run: npx nyc report --check-coverage --branches 90 --functions 90 --lines 90 --statements 90 --reporter lcov --reporter text --reporter clover -t coverage
- name: SonarCloud Scan
uses: sonarsource/sonarcloud-github-action@master
- uses: geekyeggo/delete-artifact@v1
with:
name: coverage-artifacts
failOnError: false

#GitHub #testing #jest #supertest #nodejs #typescript #istanbul #githubactions


Thank you for reading my article!
Reach me on LinkedIn.

--

--