📝

Github Actions x Vitest v4 においてテスト結果を pull request にコメントで書き込む

に公開

表題通りです。
微妙に詰まったりしたので、備忘録として載せておきます。

実装の流れと解説

Vitest を入れるプロジェクトを用意する

Vite でも Next.js でも何でも良いのですが、まずは何かしらのプロジェクトを用意します。
今回は npm create vite@latest コマンドを実行して、Vite x TypeScript プロジェクトを作成します。

Vitest v4 をセットアップする

次に npm install -D vitest コマンドを実行して、Vitest を入れます。
本記事執筆時点(2025.11.16)では、v4.0.9 をインストールできました。

vitest をインストールしたら、vitest 公式サイトにある簡単なテストを実装してみます。
次のソースコードは Vitest Getting Started > Writing Tests のサンプルを TypeScript 化したコードです。

sum.ts
export function sum(a: number, b: number): number {
  return a + b
}
sum.test.ts
import { expect, test } from 'vitest'
import { sum } from './sum'

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3)
})

次に、vitest コマンドを package.json に書き込みます。
次のソースコードも Vitest Getting Started > Writing Tests より引用です。

package.json
{
  "scripts": {
    "test": "vitest"
  }
}

Github Actions に Vitest v4 を組み込む

Github Actions で Vitest を実行できるように組み込んでいきます。
単に実行するだけなら、次の yml を用意すれば事足ります。

.github/workflows/test.yml
name: Test

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Use Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "22.13.0" # Node.js の version はお使いのプロジェクトに合わせてください。
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm run test

Github Actions のログに Vitest の結果を表示したい場合は、Vitest 公式サイト > GitHub Actions Reporter に記載されている通りで、vitest の設定ファイルを更新しましょう。

vitest.config.ts
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    // 以下を追加
    reporters: process.env.GITHUB_ACTIONS ? ['dot', 'github-actions'] : ['dot'],
  },
})

この reporters の設定を書くと https://github.com/{user名}/{リポジトリ名}/actions/runs/{process.env.GITHUB_RUN_ID} に、Vitest のテスト結果が出力されます。

Github Actions x Vitest v4 の出力結果

Github Actions で実行した Vitest の結果を pull request にコメントで書き込む

先程作成した .github/workflows/test.yml と vitest.config.ts を次のように更新します。

.github/workflows/test.yml
name: Test

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

permissions:
  contents: read       # test-result.json (後述)の読み込みに必要。
  pull-requests: write # pull-request への書き込みに必要。

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Use Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "22.13.0"
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm run test

      # ココから追記になります。
      - name: "Report Vitest Result"
        # 失敗するテストがあっても、ログは送信したいので always() にしてます。
        if: always()
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require("fs");

            // 以下は pull request 以外では発火させないための処理です。
            // if: always() と組み合わせると不要になる気がしますが、いったんコレで。
            if (!context.payload.pull_request) {
              console.log("Not running on a pull request.");
              process.exit(0);
            }

            const TEST_RESULT_PATH = "test-result.json";

            if (!fs.existsSync(TEST_RESULT_PATH)) {
              console.log(`Test result file not found at ${TEST_RESULT_PATH}.`);
              process.exit(1);
            }

            // Vitest の実行結果ファイルを読み込みます。
            const result = JSON.parse(fs.readFileSync(TEST_RESULT_PATH, "utf8"));

            if (result.numTotalTests === 0) {
              console.log("No test case.");
              process.exit(1);
            }

            const runLink = `${process.env.GITHUB_SERVER_URL || "https://github.com"}/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.GITHUB_RUN_ID}`;

            // Vitest の実行結果を pull request のコメントとしてまとめます。
            const body = `
            # テスト結果\n
            **${result.success ? "全テスト成功" : "失敗したテストあり"}**\n
            \n
            ## テストスイート\n
            - 総件数: ${result.numTotalTestSuites} 件
              - 成功: ${result.numPassedTestSuites} 件
              - 失敗: ${result.numFailedTestSuites} 件
              - 保留: ${result.numPendingTestSuites} 件
            \n
            ## テストケース\n
            - 総件数: ${result.numTotalTests} 件
              - 成功: ${result.numPassedTests} 件
              - 失敗: ${result.numFailedTests} 件
              - 保留: ${result.numPendingTests} 件
            \n
            ## 全量レポート リンク\n
            (Actions run): ${runLink}\n
            \n
            _Generated by workflow ${process.env.GITHUB_WORKFLOW || ""}_
            `;

            // まとめたコメント(body)を、pull request に書き込みます。
            await github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.payload.pull_request.number,
              body,
            });
vitest.config.ts
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    reporters: process.env.GITHUB_ACTIONS
      ? ["dot", "github-actions", "json"] // "json" を追記
      : ["dot"],
    outputFile: "test-result.json", // outputFile を追記。Vitest の実行結果を test-result.json として出力します。これを Github Actions 実行中に参照します。
  },
});

うまく jobs が実行できたなら、下記のように pull request に Vitest の実行結果が、コメントとして書き込まれます。
今回は「レポート全量は Action のリンクを確認してね。」としていますが、Vitest の report の testResults には各テストケースの詳細が全部詰まっていますので、やろうと思えば「どのテストが、どんな理由で落ちたか」を書き込むこともできます。

Vitest実行結果

まとめ

以上、「Github Actions x Vitest v4 においてテスト結果を pull request にコメントで書き込む」でした。

少々雑なスクリプトになりましたが、とりあえず、やりたいことは実現できましたのでヨシッ!とします。
細かい最適化やカスタマイズは、今後やっていこうと思います。

では、また!

Discussion