見出し画像

Laravel 11のテストをGitHub Actionsで動かす

GitHubが提供しているCI/CD環境 GitHub Actions を用いて、ブランチ/Pull Request毎にLaravelの各種テストを自動で実行させる構成を紹介します。
Freeプランでも 2,000分/月の時間無料 でGitHub Actionsを動かして試すことができます(2025-01現在)


GitHub Actionsのジョブ定義ファイル

GitHub Actionsでテストなどを自動実行させるには `.github/workflows` のディレクトリにタスクを定義したYAMLを配置します。このYAMLでLaravelの各種テストが動くように準備して、テストを走らせるコマンドを実行すればよいわけです。

  • unit-test: 単体, 機能テストを実行する( `php artisan test` )

  • e2e-test: E2Eテストを実行する(Laravel Dusk)

  • lint: コードがルールに沿って整形されているか確認する(Laravel Pint)

`./.github/workflows` にジョブのYAMLを配置します。ここでは`./.github/workflows/integration.yaml` にジョブを定義しています。
ファイルの内容は長いですがジョブは上記と同じく3つになります。

`./.github/workflows` であればYAMLのファイル名は問いません。そのため、ファイルが長くなればジョブごとに分割することもできます。

説明のため1ファイルにまとめていますが、最終的なファイルの内容は次の通りです。



name: Integration

on:
  pull_request:
    types: [opened, reopened, synchronize]
    branches:
      - main
  push:
    branches:
      - main

jobs:

  unit-test:
    name: run unit test
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.4'
          extensions: imagick, zip
      - name: Cache vendor
        id: cache
        uses: actions/cache@v3
        with:
          path: ./vendor
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: |
            ${{ runner.os }}-composer-
      - name: Composer install
        if: steps.cache.outputs.cache-hit != 'true'
        run: composer install -n --prefer-dist
      - name: Copy .env
        run: cp .env.example .env
      - name: Generate key
        run: php artisan key:generate && php artisan config:clear
      - name: Run unit test(php artisan test)
        run: php artisan test

  e2e-test:
    timeout-minutes: 60
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Act dependencies
        if: ${{ env.ACT }}
        run: |
          apt-get update && apt-get install sudo -y
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.4'
          extensions: imagick, zip
      - name: Cache vendor
        id: cache
        uses: actions/cache@v3
        with:
          path: ./vendor
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: |
            ${{ runner.os }}-composer-
      - name: Composer install
        if: steps.cache.outputs.cache-hit != 'true'
        run: composer install -n --prefer-dist
      - name: Package list update for install extensions
        run: sudo apt-get update
      - name: Install APT packages(ZIP extension)
        run: sudo apt-get install -y zlib1g-dev libzip-dev
      - name: Install APT packages(X11)
        run: sudo apt-get install -y libxpm4 libxrender1 libgtk2.0-0t64 libnss3 xvfb gtk2-engines-pixbuf xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable imagemagick x11-apps
      - name: Install APT packages(Chrome)
        run: sudo apt-get install -y wget
      - name: Install Chrome Browser
        run: sudo wget -q https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb && sudo apt-get install -y ./google-chrome-stable_current_amd64.deb && sudo rm -rf ./google-chrome-stable_current_amd64.deb
      - name: Install Laravel Dusk
        run: php artisan dusk:install
      - name: Copy .env
        run: cp .env.example .env
      - name: Generate key
        run: php artisan key:generate
      - name: start DEV server for E2E test
        run: php artisan serve > /dev/null 2>&1 &
      - name: Run E2E test(php artisan dusk)
        run: php artisan dusk

  lint:
    name: run lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.4'
          extensions: imagick, zip
      - name: Cache vendor
        id: cache
        uses: actions/cache@v3
        with:
          path: ./vendor
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: |
            ${{ runner.os }}-composer-
      - name: Composer install
        if: steps.cache.outputs.cache-hit != 'true'
        run: composer install -n --prefer-dist
      - name: Copy .env
        run: cp .env.example .env
      - name: Generate key
        run: php artisan key:generate
      - name: Run lint(Pint)
        run: vendor/bin/pint --test

on: ジョブの起動条件を設定

`job` の前にこのYAMLに沿ってGitHub Actionsでジョブを起動させる条件を `on` で設定します。こちらでは `pull_request` と `push` の2つを設定しています。

  • `pull_request`: プルリクエストに対して `types` のイベントが発生したとき

    • `types`: デフォルトでは `[opened, reopened, synchronize]`

      • `opened`: プルリクエストがオープンされたとき

      • `reopend`: プルリクエストがクローズ後再びオープンされたとき

      • `synchronize`: (簡単にいうと)プルリクエストのブランチが更新されたとき。 詳しい仕組み はブランチ, `HEAD` の概念を理解する必要がある。

    • `branches`: プルリクエストの対象となるブランチ

  • `push`: プッシュが発生したとき

    • `branches`: 対象となるブランチ

name: Integration

on:
  pull_request:
    types: [opened, reopened, synchronize]
    branches:
      - main
  push:
    branches:
      - main

unit-testジョブ: 単体(Unit), 機能(Feature)テストを実行する( `php artisan test` )

Laravelの `php artisan test` は単体, 機能テストの両方を実行します。

  • 単体テスト: メソッドの入出力といった小さな範囲でテストする

  • 機能テスト: Controllerに対し、Requestを入力、Responseを出力としてテストする

  • E2Eテスト: 対象のアプリに対してユーザーと同じインターフェース(例: Webブラウザの操作と表示)で入出力をテストする

プロジェクト立ち上げ状態では単体, 機能テスト両方のテストケースが生成されているため、 `php artisan test` ですぐに試すことができます。

GitHub Actionsで実行するには、さらにLaravel自体を実行できるようにする準備が必要になります。

  • PHPのセットアップ (`shivammathur/setup-php@v2`)

    • `shivammathur/setup-php@v2` を使うことで、Laravel 11が要求するバージョンのPHPをセットアップできます

  • Composerパッケージのインストール(`composer install -n --prefer-dist`)

    • そのまえに `actions/cache@v3` を挟み、パッケージ群をキャッシュさせることができます

    • キャッシュのキーとして `keys` を指定して、同じものがあればそれを使います

    • `keys` とは別にキャッシュ検索用のキーを前方一致で `restore-keys` で指定できます

    • `if: steps.cache.outputs.cache-hit != 'true'` という条件を付けることで、キャッシュが使える場合はComposerパッケージのインストールをスキップできます

  • GitHub Actions用に`.env` ファイルを作成する

    • `php artisan key:generate` でシークレットを生成し、そのほかの設定も済ませたファイルを `.env.github` などのファイル名でリポジトリに追加します。
      GitHub ActionsのJobではこのファイルから `.env` ファイルを作成します

    • もしくは`.env.sample` から`.env` ファイルを作成する

      • `php artisan key:generate` でシークレットを生成する必要があります

jobs:

  unit-test:
    name: run unit test
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.4'
          extensions: imagick, zip
      - name: Cache vendor
        id: cache
        uses: actions/cache@v3
        with:
          path: ./vendor
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: |
            ${{ runner.os }}-composer-
      - name: Composer install
        if: steps.cache.outputs.cache-hit != 'true'
        run: composer install -n --prefer-dist
      - name: Copy .env
        run: cp .env.example .env
      - name: Generate key
        run: php artisan key:generate && php artisan config:clear
      - name: Run unit test(php artisan test)
        run: php artisan test

e2e-testジョブ: E2Eテストを実行する(Laravel Dusk)

Laravel Duskの初回設定

E2EテストはLaravel DuskというLaravelの機能を用いますが、初回はComposerでパッケージをプロジェクトに追加する必要があります。さらにこの後追加の設定も初回で必要です。

$ composer require --dev laravel/dusk

次に、 Laravel Duskに必要なテストの設定を生成します。

php artisan dusk:install

'Class "ZipArchive" not found' と表示される場合がありますが、必要なファイルは作成されています。

先ほどのコマンドはファイルの作成に加え、Laravel DuskからWebブラウザを操作してテストするためのドライバーインストールします。
このエラーが出る場合はドライバーのインストールに失敗しています。

開発環境では初期のファイルを生成するために`php artisan dusk:install`を実行します。
GitHub Actions上ではなく開発環境上で今すぐLaravel Duskを実行するのでなければ問題ありません。

`php artisan dusk:install` で下記が見つからないと新規作成されます。

  • `./tests/Browser`: 簡単なテストケースも含みます(`./tests/Browser/ExampleTest.php`)

  • `./tests/DuskTestCase.php`: テスト操作用に起動するブラウザの設定などができます

`./tests/DuskTestCase.php` で次の設定を追加します。

    protected function driver(): RemoteWebDriver
    {
        $options = (new ChromeOptions)->addArguments(collect([
            $this->shouldStartMaximized() ? '--start-maximized' : '--window-size=1920,1080',
            '--disable-search-engine-choice-screen',
        ])->unless($this->hasHeadlessDisabled(), function (Collection $items) {
            return $items->merge([
                '--disable-gpu',
                '--headless=new',

                // 下記を追加
                '--no-sandbox',
                '--disable-dev-shm-usage'
            ]);
        })->all());

2025-01現在上記の設定を追加しないと下記のエラーが発生してE2Eテストを開始できません。

session not created: Chrome failed to start: exited normally.   (session not created: DevToolsActivePort file doesn't exist)

これらの変更を リポジトリに反映させてから Laravel Duskが使えるようになります。

GitHub ActionsでLaravel Duskを起動するためにジョブで必要な準備

さらに、GitHub ActionsのジョブではLaravel自体を実行できるようにする準備に加え、次の準備も 追加で 必要になります。

  • Webブラウザのインストール

    • OSパッケージのアップデート

    • ZIP, X11のライブラリをインストール

  • Laravel Duskで使用するWebブラウザのドライバインストール( `php artisan dusk:install` )

  • E2Eテストのターゲットとなるアプリの起動( `php artisan serve` )

    • 次のコマンドを実行できるようにアプリをバックグラウンドで起動させる必要があります。
      ここでは `php artisan serve > /dev/null 2>&1 &` を実行します。

`.github\workflows\integration.yaml` の抜粋で、E2Eテストに使用するWebブラウザを動かすためにはこれだけ必要になります。

      - name: Package list update for install extensions
        run: sudo apt-get update
      - name: Install APT packages(ZIP extension)
        run: sudo apt-get install -y zlib1g-dev libzip-dev
      - name: Install APT packages(X11)
        run: sudo apt-get install -y libxpm4 libxrender1 libgtk2.0-0t64 libnss3 xvfb gtk2-engines-pixbuf xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable imagemagick x11-apps
      - name: Install APT packages(Chrome)
        run: sudo apt-get install -y wget
      - name: Install Chrome Browser
        run: sudo wget -q https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb && sudo apt-get install -y ./google-chrome-stable_current_amd64.deb && sudo rm -rf ./google-chrome-stable_current_amd64.deb

lintジョブ: コードのスタイルチェック(Laravel Pint)

Laravel PintはLaravelでプロジェクトを立ち上げると標準で付属します。
ですが、実行するためにはLaravel Pintの設定ファイル `./pint.json` が必要になります。

Laravel Pintの導入を紹介する記事
https://qiita.com/matsunoeda/items/f11dc8ba19e49381296f
から、「PHPDoc関連のコードを削除しない設定」を追加します。`./pint.json`の内容は次のとおりです。

{
    "preset": "laravel",
    "rules": {
        "no_superfluous_phpdoc_tags": false
    }
}

`./pint.json`をリポジトリに含めてLaravel Pintを使えるようにします。

`vendor/bin/pint` でLaravel Pintを実行できます。
コードがルールに沿って整形されているか確認する目的で使用しますが、Laravel Pintは整形の機能もあります。 `--test` オプションを付けて整形せずに確認のみにします。

vendor/bin/pint --test

おわりに

以上を組み合わせると 単体/機能テスト, E2Eテスト, コードのスタイルチェックをLaravelの標準機能(E2Eテストは追加のパッケージが必要)で揃えて、GitHub Actionsに任せることができます。
GitHubリポジトリの操作やプルリクエストにGitHub Actionsの実行結果をつなげることでレビューの負荷を抑えることも期待されます。

いいなと思ったら応援しよう!