New Relicでテストカバレッジ(Jest)を可視化してみた

## 概要
本記事ではNew RelicのEvent APIを使って、アプリケーションのテストコードのカバレッジ可視化を行う方法を紹介します。

### 前提
- テストツールとしては**Jest**を使用
- テストコードのカバレッジ取得は**GitHub Actions**で行う
    - 本記事ではself-hosted runnerを利用した方法を紹介します

※ GitHub ActionsやNew Relicでのカスタムイベント等に関する説明は今回割愛させていただきます


## 完成イメージ
![スクリーンショット 2023-12-04 10.45.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689205/096c80ea-e934-b1f4-37fb-1584451b99fc.png)
![スクリーンショット 2023-12-04 11.23.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689205/4dd24f89-f37f-d44f-e782-0074ed3d87e8.png)



## 実装

### (1) GitHub Actions　ワークフロー

`.github/workflows/` 配下にymlファイルを作成します。
今回はmainブランチにPRをマージした際に実行されるように設定します。

```yml
name: 'Send Coverage Report To NewRelic'

on:
  pull_request:
    branches: [main]
    types:
      - closed

jobs:
  send-coverage-report-to-newrelic:
    if: >-
      github.ref_name == 'main' && 
      github.event.pull_request.merged == true
    runs-on:
      labels: [self-hosted]
    steps:
      - name: checkout
        uses: actions/checkout@v3

      - name: Node Setup
        uses: actions/setup-node@v3.1.1
        with:
          node-version-file: '.node-version'

      - name: npm install -g yarn
        run: npm install -g yarn

      - name: yarn install
        run: yarn install

      - name: Send To New Relic
        shell: bash
        run: bash ./.github/scripts/record-jest-coverage.sh
        env:
          GH_REPO: ${{ github.repository }}
          EVENT_TYPE: JestCoverage
          PR_TITLE: ${{ github.event.pull_request.title }}
          NEW_RELIC_ACCOUNT_ID: ${{ vars.NEW_RELIC_ACCOUNT_ID }}
          NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}

      # self-hosted runnerを利用している場合、ストレージを圧迫しないように
      # 「依存関係のクリーンアップ」 「キャッシュのクリア」を実行しておく
      - name: 'Cleanup Repository Folders'
        run: |
          ls -la ./
          rm -rf ./* || true
          git clean -fdx
      - name: yarn cache clean
        run: yarn cache clean
```

### (2) シェルスクリプトの作成
Jestを実行し、カバレッジ取得結果をNew Relicにカスタムイベントとして送信するスクリプトを作成します
```.github/scripts/record-jest-coverage.sh

#!/bin/bash

# yarn test --coverageの実行例
# -----------------------------|---------|----------|---------|---------|-------------------
# File                         | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
# -----------------------------|---------|----------|---------|---------|-------------------
# All files                    |   94.44 |      100 |      75 |   93.75 |                   
#  src/lib                     |     100 |      100 |     100 |     100 |                   
#   xxxx.tsx                   |     100 |      100 |     100 |     100 |                   
#  src/components              |   88.88 |      100 |      50 |    87.5 |                   
#   xxxxxxxxx.tsx              |   88.88 |      100 |      50 |    87.5 | 27                
#  src/components/XXXXXXXXXXXX |     100 |      100 |     100 |     100 |                   
#   index.tsx                  |     100 |      100 |     100 |     100 |                   
# -----------------------------|---------|----------|---------|---------|-------------------

# yarn test --coverageの実行結果を取得
RESULT=`yarn test --coverage | \
  tee | \
  sed -E "s/"$'\E'"\[([0-9]{1,2}(;[0-9]{1,2})*)?m//g"
`

# yarn test --coverageの実行結果を格納

list=(${RESULT//|/})
# 格納される内容（例）
# All files 94.44 100 75 93.75 app/form 100 100 100 100 page.tsx 100 100 100 100 src/components 88.88 100 50 87.5


DIR_NAME=''
i=0
ARR=()

function send_newrelic () {
  if [[ -n "$DIR_NAME" ]] && [[ ${#ARR[*]} -gt 3 ]]; then
    curl -X POST "https://insights-collector.newrelic.com/v1/accounts/$NEW_RELIC_ACCOUNT_ID/events" \
    -H "Api-Key:$NEW_RELIC_LICENSE_KEY" \
    -H "Content-Type: application/json" \
    -d \
    "{
      \"eventType\": \"${EVENT_TYPE}\",
      \"directoryName\": \"${DIR_NAME}\",
      \"statements\": \"${ARR[0]}\",
      \"branch\": \"${ARR[1]}\",
      \"funcs\": \"${ARR[2]}\",
      \"lines\": \"${ARR[3]}\",
      \"prTitle\": \"${PR_TITLE}\",
      \"repositoryName\": \"${GH_REPO}\"
    }"
  fi
}

for S in "${list[@]}"
do 

  # 各ディレクトリのStatements, Branch, Funcs, Linesのカバレッジ数値を取得する
  if [[ "$S" =~ ^-?[1-9]*[0-9](\.[0-9]+)?$ ]] || [[ "$S" =~ ^[0-9]+$ ]]; then
    
    if [[ $i -lt 4 ]]; then
      ARR[$i]=$S
      i=`expr $i + 1`
    fi

  # ALL FILE取得(一番最初に取得される)
  elif [[ "$S" =~ All ]]; then
    DIR_NAME="All files"

  # ディレクトリ名取得
  elif [[ "$S" =~ components/|hooks|lib/|utils/ ]]; then
    send_newrelic

    DIR_NAME=$S
    FILE_NAME=''
    i=0
    ARR=()

  fi
done

send_newrelic
```

### (3) シークレット・環境変数の設定
GitHubのリポジトリのSettingからNew Relicのライセンスキー・アカウントIDをそれぞれシークレット・変数として設定しておきます。

![スクリーンショット 2023-12-04 11.35.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689205/c319330f-f3e9-fab5-5ec8-39633fb0a87e.png)


### (4) ダッシュボードの作成

New Relic > Dashboards で新規ダッシュボードを作成します。

(i) 全てのファイルの最新カバレッジ率
```nrql
SELECT average(numeric(statements)) as 'Statements', 
average(numeric(funcs)) as 'Functions', 
average(numeric(branch)) as 'Branch', 
average(numeric(lines)) as 'Lines' 
from JestCoverage 
where directoryName = 'All files' 
Limit 1
```

![スクリーンショット 2023-12-04 10.45.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689205/99edc6d1-2ca6-adc8-e0d9-7e6060605145.png)



(ii) カバレッジの推移
```nrql
SELECT average(numeric(statements)) as 'Statements', 
average(numeric(funcs)) as 'Functions', 
average(numeric(branch)) as 'Branch', 
average(numeric(lines)) as 'Lines' 
from JestCoverage 
where directoryName = 'All files' 
TIMESERIES 1 week since 1 month ago
```

![スクリーンショット 2023-12-04 11.23.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689205/09cd6b30-8a52-1a1f-1b09-ec13dceac4c4.png)


## おわりに
PRごとにテストカバレッジを可視化し、定期的にチームで振り返ることで、「網羅性の高いテストコードを書く」という意識が強化されるかもしれません。

**「カバレッジ率80％を維持する」** といった目標を設定したり、
**「一定値を下回ったらSlackに通知が飛ぶように設定する」** などといったカスタマイズをするのも良いと思います！

「テストコードを全然書けていないな」といった課題があったら、ぜひ試してみてはいかがでしょうか。

## 参考資料

New Relic Event APIの利用方法

https://docs.newrelic.com/jp/docs/data-apis/ingest-apis/event-api/introduction-event-api/