はじめに
自動テストにおけるカバレッジにおいて、ぱっと見でどこの行が通っていないかというのを視覚的に確認できるツールは有用です。
しかし、そういった情報を認識するための構造は大体がHTMLであり、GitHubのPRで確認するには「視覚情報を除いた文字情報で表示する・Artifactなどにアップしたものをダウンロードする」必要があります。
「視覚情報が除かれるのは本末転倒、ダウンロードするというのは面倒くさい」これらの解決をモチベーションに、カバレッジレポートをCloudflareへデプロイし、URLをコメントしてくれるようなWorkflowが欲しかったので作ってみました。
注意事項
本記事ではCloudflareのアクセス制御(ZeroTrust)などについては特に触れませんが、実PJに導入する場合には条件に合わせてアクセス制御の考慮をする必要があるので注意してください。
PJのセットアップ
まずはテストが実行できる環境を作ります。
今回は vitest を使用しますが、HTMLでレポートが出力できればなんでも大丈夫です。
※ 既に環境がある場合には Workflowの作成 まで飛ばして大丈夫です。
必要packageのインストール
PJを作成してから必要なpackageをインストールします。
pnpm init
pnpm add -D vitest typescript @vitest/coverage-v8
コマンドをscriptsへ登録
test:coverage をscripts配下へ登録します。
{
"name": "test-example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test:coverage": "vitest run --coverage"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.23.0",
"devDependencies": {
"@vitest/coverage-v8": "^4.0.18",
"typescript": "^5.9.3",
"vitest": "^4.0.18"
}
}
tsconfig.jsonの作成
tsconfig.json を作成します。
touch tsconfig.json
最小構成として以下の内容で保存します。
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"skipLibCheck": true,
"isolatedModules": true,
"noEmit": true,
"types": ["vitest/globals"]
},
"include": ["src/**/*", "tests/**/*"]
}
vitest.config.tsの作成
vitest.config.ts を作成します。
touch vitest.config.ts
最小構成として以下の内容で保存します。
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
coverage: {
reporter: ['json-summary', 'html']
}
}
});
reporter として html 以外にも json-summary を登録していますが、後ほど作成するWorkflowのPRコメントへのサマリーで使用するもので、不要であれば消して問題ありません。
テストファイルの作成
テストが実行できるためのファイルが必要なのでChatGPTなどで適当に作成しておきます。
※ 結果が見れれば良いだけなので妥当性などは気にしません。
テスト対象:
export function add(a: number, b: number): number {
return a + b;
}
export function subtract(a: number, b: number): number {
return a - b;
}
export function multiply(a: number, b: number): number {
return a * b;
}
export function divide(a: number, b: number): number {
if (b === 0) {
throw new Error('Division by zero is not allowed');
}
return a / b;
}
テスト:
import { add, subtract, multiply, divide } from '../src/calculator';
describe('calculator', () => {
it('add', () => expect(add(1, 2)).toBe(3));
it('subtract', () => expect(subtract(3, 1)).toBe(2));
it('multiply', () => expect(multiply(2, 3)).toBe(6));
it('divide', () => expect(divide(6, 2)).toBe(3));
});
テストの実行
以下コマンドでテストを実行し、以下のような結果になればOKです。
※ あえて100%にならないように調整しています。
pnpm test:coverage
▼ 結果
---------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
---------------|---------|----------|---------|---------|-------------------
All files | 83.33 | 50 | 100 | 83.33 |
calculator.ts | 83.33 | 50 | 100 | 83.33 | 15
---------------|---------|----------|---------|---------|-------------------
Workflowの作成
ローカルでテストが実行できる環境ができたので、次はCI環境でテストが実行できるようにしていきます。
name: Test
on:
pull_request:
types:
- opened
- synchronize
- reopened
jobs:
test-and-deploy:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
pull-requests: write
contents: read
steps:
- name: ソースのチェックアウト
uses: actions/checkout@v4
- name: pnpmのセットアップ
uses: pnpm/action-setup@v4
- name: nodeのセットアップ
uses: actions/setup-node@v4
id: setup-node
with:
node-version: '22'
cache: 'pnpm'
- name: 依存関係のインストール
run: pnpm install --frozen-lockfile
- name: テストを実行(カバレッジレポート付き)
run: pnpm test:coverage
- name: コメントを作成
id: comment
run: |
# coverage-summary.json から値を取得
LINES=$(cat ./coverage/coverage-summary.json | jq -r '.total.lines.pct // 0')
STATEMENTS=$(cat ./coverage/coverage-summary.json | jq -r '.total.statements.pct // 0')
BRANCHES=$(cat ./coverage/coverage-summary.json | jq -r '.total.branches.pct // 0')
FUNCTIONS=$(cat ./coverage/coverage-summary.json | jq -r '.total.functions.pct // 0')
# コメント本文を作成
cat << EOF >> $GITHUB_OUTPUT
body<<MARKER
## テストカバレッジ
| 項目 | カバー率 |
|:-----|--------:|
| 行(Lines) | ${LINES}% |
| ステートメント(Statements) | ${STATEMENTS}% |
| 分岐(Branches) | ${BRANCHES}% |
| 関数(Functions) | ${FUNCTIONS}% |
MARKER
EOF
- name: コメントを投稿
uses: marocchino/sticky-pull-request-comment@v2
with:
header: カバレッジサマリー
message: ${{ steps.comment.outputs.body }}
基本的にはstepで書いている通りのことをしています。
ですが「コメントを作成」では、テスト実行時に生成される ./coverage/coverage-summary.json を読み込んでサマリーを作成しています。
サマリーの表示をカスタマイズする際には基本的にこの箇所を更新していくような形になります。
ではこの状態で別ブランチから現在の内容をpushし、PRを出してみます。
すると、以下画像のようなPRコメントが投稿されることが確認できます。
Cloudflareへの自動デプロイ
ここまででテストの実行環境は整備できました。
ではここからはやっと本題に入り、Cloudflareへカバレッジレポートをデプロイしていきます。
事前準備
事前準備としてCloudflareAPIトークンが必要になるので準備していきます。
まずはCloudflareの プロフィール へ移動し、サイドバー > APIトークンと選択します。
移動すると「トークンを作成する」ボタンがあるのでクリックし「Cloudflare Workers を編集する」というテンプレートを選択します。
選択すると色々な権限がセット済みの画面が表示されますが、今回はデプロイのみ行えればよいので「Workers スクリプト」のみ残し、他は全部削除してしまって問題ありません。
アカウントリソースについては、自身のアカウントを選択(含む)、TTLはこのトークンを使用する期間に応じて設定してください。
ここまで作成出来たら 概要に進む > トークンを作成 で作成してください。次の画面で出てくるトークンをメモしておきます。
※ 1度しか出てこないので注意が必要です。(忘れた場合は作り直してください)
Workflowへの追記
先ほど作成した既存のWorkflowへ以下のように追記します。
name: Test
on:
pull_request:
types:
- opened
- synchronize
- reopened
jobs:
test-and-deploy:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
pull-requests: write
contents: read
steps:
- name: ソースのチェックアウト
uses: actions/checkout@v4
- name: pnpmのセットアップ
uses: pnpm/action-setup@v4
- name: nodeのセットアップ
uses: actions/setup-node@v4
id: setup-node
with:
node-version: '22'
cache: 'pnpm'
- name: 依存関係のインストール
run: pnpm install --frozen-lockfile
- name: テストを実行(カバレッジレポート付き)
run: pnpm test:coverage
+ - name: Cloudflareにデプロイ
+ id: deploy
+ uses: cloudflare/wrangler-action@v3
+ with:
+ apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
+ accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
+ command: deploy --assets ./coverage --name test-coverage --compatibility-date 2026-02-15
+ - name: バージョンIDを抽出
+ id: deploy-version
+ env:
+ DEPLOY_OUTPUT: ${{ steps.deploy.outputs.command-output }}
+ run: |
+ VERSION=$(
+ echo "$DEPLOY_OUTPUT" \
+ | grep -oP 'Current Version ID: \K[0-9a-f-]+' \
+ | cut -d'-' -f1
+ )
+ echo "version=${VERSION}" >> $GITHUB_OUTPUT
- name: コメントを作成
id: comment
run: |
# coverage-summary.json から値を取得
LINES=$(cat ./coverage/coverage-summary.json | jq -r '.total.lines.pct // 0')
STATEMENTS=$(cat ./coverage/coverage-summary.json | jq -r '.total.statements.pct // 0')
BRANCHES=$(cat ./coverage/coverage-summary.json | jq -r '.total.branches.pct // 0')
FUNCTIONS=$(cat ./coverage/coverage-summary.json | jq -r '.total.functions.pct // 0')
+ # Preview URLを生成
+ DEPLOYMENT_URL="${{ steps.deploy.outputs.deployment-url }}"
+ VERSION="${{ steps.deploy-version.outputs.version }}"
+ PREVIEW_URL=$(echo "$DEPLOYMENT_URL" | sed "s|https://|https://${VERSION}-|")
# コメント本文を作成
cat << EOF >> $GITHUB_OUTPUT
body<<MARKER
## テストカバレッジ
| 項目 | カバー率 |
|:-----|--------:|
| 行(Lines) | ${LINES}% |
| ステートメント(Statements) | ${STATEMENTS}% |
| 分岐(Branches) | ${BRANCHES}% |
| 関数(Functions) | ${FUNCTIONS}% |
+ **プレビューURL**: ${PREVIEW_URL}
MARKER
EOF
- name: コメントを投稿
uses: marocchino/sticky-pull-request-comment@v2
with:
header: カバレッジサマリー
message: ${{ steps.comment.outputs.body }}
追記したstepを順に説明すると、以下は cloudflare/wrangler-action@v3 を使用して、指定した name でデプロイを行い、--assets には ./coverage を指定することで生成したカバレッジレポートをそのままデプロイするようにしています。
また、デプロイには後述しますが先ほど設定したAPIトークンが必要となります。
- name: Cloudflareにデプロイ
id: deploy
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --assets ./coverage --name test-coverage --compatibility-date 2026-02-15
次はPRへプレビューURLをコメントするためのURLを生成するために必要なversionを特定します。
Cloudflareのバージョン用URLは https://{version}-{name}.{subdomain}.workers.dev のようになっており、versionはデプロイ時に表示される、 Current Version ID: cfb94a42-907f-47a2-b0f6-0d41c0d87285 内の、頭8桁部分が使用されます。
これを利用して、以下stepでは前のstepの出力から正規表現で - で区切るようにし、マッチした中の最初のフィールドのみを取得するようにしています。
- name: バージョンIDを抽出
id: deploy-version
env:
DEPLOY_OUTPUT: ${{ steps.deploy.outputs.command-output }}
run: |
VERSION=$(
echo "$DEPLOY_OUTPUT" \
| grep -oP 'Current Version ID: \K[0-9a-f-]+' \
| cut -d'-' -f1
)
echo "version=${VERSION}" >> $GITHUB_OUTPUT
※ 最初にenvへ steps.deploy.outputs.command-output を入れる理由は、そのまま使用すると一部内容をそのままコマンドとして解釈しようとしてしまうため、一度環境変数へ設定しています。
最後にここまで使用した情報を元にプレビュー用のURLを作成してコメントするようにしています。
steps.deploy.outputs.deployment-url が突然出てきたように見えますが、 cloudflare/wrangler-action@v3 で提供されている機能を使用しています。
これが返却してくれるURLは https://{worker-name}.{subdomain}.workers.dev となっているため、sed コマンドを使用して、 https:// の部分を https://${VERSION} に置換しています。
# Preview URLを生成
DEPLOYMENT_URL="${{ steps.deploy.outputs.deployment-url }}"
VERSION="${{ steps.deploy-version.outputs.version }}"
PREVIEW_URL=$(echo "$DEPLOYMENT_URL" | sed "s|https://|https://${VERSION}-|")
Secretsの設定
後はこのWorkflow内で登場する CLOUDFLARE_API_TOKEN と CLOUDFLARE_ACCOUNT_ID の設定をします。
Workflowが実行されるリポジトリにアクセスし、 Settings > Secrets and variables > New repository secret を選択します。
選択すると、 Name と Secret を入力するよう求められるので、 CLOUDFLARE_API_TOKEN には先ほどメモしたAPIトークンを入力します。
CLOUDFLARE_ACCOUNT_ID については、アカウントのダッシュボードへ移動し、アカウント名の三点リーダーから「アカウントIDをコピー」でコピーした値を入力してください。
実行
ここまでできたら後は先ほどpushしたブランチに再度現在の内容をpushし直してください。
そうすると、以下のようにプレビュー用のURLが投稿されるようになり、アクセスするとカバレッジレポートが表示されることが確認できると思います。
おわりに
今回はGitHub Actionsで実行したテストのカバレッジレポートをCloudflare Workersへデプロイし、PRコメントから直接アクセスできるようにするWorkflowを作成しました。
これにより、Artifactをダウンロードしてローカルで展開するといった手間がなくなり、PR上でワンクリックでカバレッジレポートを確認できるようになります。
レビューを行う際に、変更内容に対してテストがどの程度カバーできているかを素早く確認できるのは便利でした。
Cloudflare Workersの無料枠も十分に大きく、カバレッジレポート程度の静的アセットであればコストを気にせず運用できるのも嬉しいポイントです。
長くなりましたがここまで閲覧いただきありがとうございました。



