A screenshot of the GitHub actions pipeline for a Laravel Livewire test suite passing.

The Issue We’re Resolving

When running a Laravel test suite with Livewire components, the default workflow file provided by GitHub for Laravel likely won’t pass. When using their default config, I was presented with errors such as this:

  FAILED  Tests\Feature\Livewire\ContactFormTest > it displays on contact p…   
  Expected: <!DOCTYPE html>\n
  <html lang="en" class="auto">\n
  <!--\n
  ... (349 more lines)

  To contain: "name":"contactform"

  at vendor/livewire/livewire/src/Features/SupportTesting/SupportTesting.php:54
     50▕                 $component = app(ComponentRegistry::class)->getName($component);
     51▕             }
     52▕             $escapedComponentName = trim(htmlspecialchars(json_encode(['name' => $component])), '{}');
     53▕ 
  ➜  54▕             \PHPUnit\Framework\Assert::assertStringContainsString(
     55▕                 $escapedComponentName,
     56▕                 $this->getContent(),
     57▕                 'Cannot find Livewire component ['.$component.'] rendered on page.'
     58▕             );

      +4 vendor frames 
  5   tests/Feature/Livewire/ContactFormTest.php:19

The test in question was simply checking if the component was rendered when a certain page was visited. In this example, as I’m testing my contact form, I’m checking that the contact form is displayed on the contact route:

public function test_it_displays_on_contact_page(): void
{
    $this->get(route('contact'))
        ->assertSeeVolt('contactform');
}

Why Livewire Tests Fail in GitHub Actions

It turns out that the issue in this case was that Livewire comes in two parts: PHP and Javascript. Without the Javascript, it simply won’t work. With that in mind, this is the default workflow GitHub provides for running Laravel tests (As of 19th August 2024):

name: Laravel

on:
  push:
    branches: [ "master" ]
  pull_request:
    branches: [ "master" ]

jobs:
  laravel-tests:

    runs-on: ubuntu-latest

    steps:
    - uses: shivammathur/setup-php@15c43e89cdef867065b0213be354c2841860869e
      with:
        php-version: '8.0'
    - uses: actions/checkout@v4
    - name: Copy .env
      run: php -r "file_exists('.env') || copy('.env.example', '.env');"
    - name: Install Dependencies
      run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
    - name: Generate key
      run: php artisan key:generate
    - name: Directory Permissions
      run: chmod -R 777 storage bootstrap/cache
    - name: Create Database
      run: |
        mkdir -p database
        touch database/database.sqlite
    - name: Execute tests (Unit and Feature tests) via PHPUnit/Pest
      env:
        DB_CONNECTION: sqlite
        DB_DATABASE: database/database.sqlite
      run: php artisan test

You’ll notice that this only accommodates the PHP side of Livewire. Everything is set up appropriately for a default Laravel installation, but Livewire introduces some extra complexity which this isn’t set up to handle appropriately.

How to Fix Livewire Tests in GitHub Actions

Here is my /.github/workflows/laravel.yml file for HermitClock, my most recent Laravel/Livewire project:

name: Laravel

on:
  push:
    branches: [ "master" ]
  pull_request:
    branches: [ "master" ]

jobs:
  laravel-tests:

    runs-on: ubuntu-latest

    steps:
    - uses: shivammathur/setup-php@v2
      with:
        php-version: '8.2'
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: 21
    - name: Copy .env
      run: php -r "file_exists('.env') || copy('.env.example', '.env');"
    - name: Install PHP Dependencies
      run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
    - name: Install Node Dependencies
      run: |
        npm ci
        npm run build
    - name: Generate key
      run: php artisan key:generate
    - name: Directory Permissions
      run: chmod -R 777 storage bootstrap/cache
    - name: Create Database
      run: |
        mkdir -p database
        touch database/database.sqlite
    - name: Execute tests via PHPUnit
      env:
        APP_ENV: testing
        DB_CONNECTION: sqlite
        DB_DATABASE: database/database.sqlite
      run: php artisan test

As you can see, I’ve made some tweaks to the default configuration, as well as added some extra logic for Node.js to run:

  • php-version: '8.2 is now set to match my production environment.
  • - uses: actions/setup-node@v4 utilises the community template for introducing Node into your pipeline.
  • node-version: 21 specifies the version of Node you require. I’ve set this to 21 as this is the version my production environment uses.
  • npm run build The build command which is being run refers to my production build script in package.json.
  • APP_ENV: testing is used to ensure that Laravel is aware that it’s a testing environment, as this can affect how tests run.

Aside from these changes, the rest should be fairly self-explanatory. You will likely need to tweak the PHP and Node versions which I’ve set for my project. If you use any extra environment variables, you’ll likely need to set those too in an appropriate, secure manner.

Leave a Reply

Your email address will not be published. Required fields are marked *