How to Automatically Run Your Laravel PestPHP Tests on Each GitHub Pull Request?
Tags: laravel, php, github, testing
Published: 2021-09-18 07:35:13

# Introduction

PestPHP was created by [Nuno Maduro](https://twitter.com/enunomaduro) who is also one of the Laravel core team members. PestPHP is an open-source PHP Testing framework created with simplicity in mind. 

PestPHP is being really actively developed and there are already plenty of learning materials online despite the fact that it is relatively new.

In this tutorial, you will learn how to use GitHub actions in order to automate your PestPHP tests and run them on each pull request.

![Automated Pest PHP tests with GitHub actions](https://imgur.com/MBVpWq2.png)

# Prerequisites

Before you start, you would need to have a Laravel application up and running.

I will be using a DigitalOcean Ubuntu Droplet for this demo. If you wish, you can use my affiliate code to get [free $100 DigitalOcean credit](https://m.do.co/c/2a9bba940f39) to spin up your own servers!

If you do not have that yet, you can follow the steps from this tutorial on how to do that:

* [How to Install Laravel on DigitalOcean with 1-Click](https://devdojo.com/bobbyiliev/how-to-install-laravel-on-digitalocean-with-1-click)

Or you could use this awesome script to do the installation:

* [LaraSail](https://devdojo.com/episode/laravel-on-digital-ocean-with-larasail)

# Introductin to GitHub Actions

[GitHub Actions](https://github.com/features/actions) allow you to automate your workflow. Thanks to GitHub Actions we basically have a built in CI/CD tool directly into GitHub.

If you have never used GitHub Actions, I would recommend going through this short video here:

{% youtube GqzaCZyUlzE %}

# Automatically Run your PestPHP tests

First let's start by creating the following directories in your GitHub project:

```
mkdir .github/workflows/
```

Then create a yaml file in that directory called `tests.yaml` for example:

```
touch .github/workflows/tests.yaml
```

And add the following content:

```
name: Test Laravel Github action
on:
  pull_request:
    branches:
      - main
      - develop
 
jobs:
  laravel-tests:
    runs-on: ubuntu-latest
 
    strategy:
      matrix:
        operating-system: [ubuntu-latest]
        php-versions: [ '8.0','7.4' ]
        dependency-stability: [ prefer-stable ]
 
    name: P${{ matrix.php-versions }} - L${{ matrix.laravel }} - ${{ matrix.dependency-stability }} - ${{ matrix.operating-system}}
 
    steps:
    - uses: actions/checkout@v2
    - name: Install PHP versions
      uses: shivammathur/setup-php@v2
      with:
        php-version: ${{ matrix.php-versions }}
    - name: Get Composer Cache Directory 2
      id: composer-cache
      run: |
        echo "::set-output name=dir::$(composer config cache-files-dir)"
     - uses: actions/cache@v2
      id: actions-cache
      with:
        path: ${{ steps.composer-cache.outputs.dir }}
        key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
        restore-keys: |
          ${{ runner.os }}-composer-
     - name: Cache PHP dependencies
      uses: actions/cache@v2
      id: vendor-cache
      with:
        path: vendor
        key: ${{ runner.OS }}-build-${{ hashFiles('**/composer.lock') }}
    - name: Copy .env
      run: php -r "file_exists('.env') || copy('.env.example', '.env');"
    - name: Install Dependencies
      if: steps.vendor-cache.outputs.cache-hit != 'true'
      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: Run Migrations
# Set environment
      env:
        SESSION_DRIVER: array
        DB_CONNECTION: sqlite
        DB_DATABASE: ":memory:"
 
      run: php artisan migrate
 
    - name: Show dir
      run: pwd
    - name: PHP Version
      run: php --version
 
# Code quality
 
    - name: Execute tests (Unit and Feature tests) via PestPHP
# Set environment
      env:
        SESSION_DRIVER: array
        DB_CONNECTION: sqlite
        DB_DATABASE: ":memory:"
 
      run: vendor/bin/pest
```

With that, everytime now anyone submits a PR to your `main` or `develop` branches, your Pest PHP test will be automatically triggered!

The above uses SQLite, let's see how we could use MySQL instead!

# Automatically Run your PestPHP tests with MySQL

In some cases, you might also want to use MySQL rather than SQLite when running your tests. This could happen in case that you have some more complex queries not using Laravel Eloquent and you are seeing incorrect results when using SQLite.

In order to do that, you could use the following configuration.

First create the following directories in your GitHub project:

```
mkdir .github/workflows/
```

Then create a yaml file called `tests.yaml` for example:

```
touch .github/workflows/tests.yaml
```

And add the following content:

```
name: Laravel Tests
on:
  pull_request:
    branches:
      - main
 
jobs:
  laravel-tests:
    runs-on: ubuntu-latest
# Service container Mysql mysql
    services:
      # Label used to access the service container
      mysql:
        # Docker Hub image (also with version)
        image: mysql:5.7
        env:
          MYSQL_ROOT_PASSWORD: secret
          MYSQL_DATABASE:  testing
        ## map the "external" 33306 port with the "internal" 3306
        ports:
          - 33306:3306
        # Set health checks to wait until mysql database has started (it takes some seconds to start)
        options: >-
          --health-cmd="mysqladmin ping"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=3
 
    strategy:
      matrix:
        operating-system: [ubuntu-latest]
        php-versions: [ '7.4' ]
        dependency-stability: [ prefer-stable ]
 
    name: P${{ matrix.php-versions }} - L${{ matrix.laravel }} - ${{ matrix.dependency-stability }} - ${{ matrix.operating-system}}
 
    steps:
    - uses: actions/checkout@v2
    - name: Install PHP versions
      uses: shivammathur/setup-php@v2
      with:
        php-version: ${{ matrix.php-versions }}
    - name: Get Composer Cache Directory 2
      id: composer-cache
      run: |
        echo "::set-output name=dir::$(composer config cache-files-dir)"
    - uses: actions/cache@v2
      id: actions-cache
      with:
        path: ${{ steps.composer-cache.outputs.dir }}
        key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
        restore-keys: |
          ${{ runner.os }}-composer-
    - name: Cache PHP dependencies
      uses: actions/cache@v2
      id: vendor-cache
      with:
        path: vendor
        key: ${{ runner.OS }}-build-${{ hashFiles('**/composer.lock') }}
    - name: Copy .env
      run: php -r "file_exists('.env') || copy('.env.example', '.env');"
    - name: Install Dependencies
      if: steps.vendor-cache.outputs.cache-hit != 'true'
      run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist

    - name: Create storage folders
      run: mkdir -p storage/framework/{sessions,views,cache}
    - name: Directory Permissions
      run: chmod -R 777 storage bootstrap/cache

    - name: Show dir
      run: pwd
    - name: PHP Version
      run: php --version

# Code quality
    - name: Execute tests (Unit and Feature tests) via PestPHP
# Set environment
      env:
        DB_CONNECTION: mysql
        DB_DATABASE: testing
        DB_PORT: 33306
        DB_USER: root
        DB_PASSWORD: secret
      run: |
          php artisan key:generate
          vendor/bin/pest
```

The above example uses PHP 7.4 with MySQL 5.7. Make sure to adjust this so that this matches your needs.

How the action gets triggered is specified in the very beginning of the file:
```
name: Laravel Tests
on:
  pull_request:
    branches:
      - main
```
First we specify the name, and then we specify that we want to run this action `on` a `pull_request` for the `main` branch. Make sure to adjust the name of the branch so that it matches your setup.

Then we specify that we want to to use a MySQL container and we wait for the service to be up and running before we proceed.

If you wanted to see this in action, make sure to check out this repository here:

[PestPHP GitHub Actions Demo](https://github.com/thedevdojo/wave)

It includes some Pest tests along with a GitHub action that runs on each pull request towards the `main` branch.

# GitHub Actions configurator for your Laravel Application

If you are getting overwhelmed looking at the GitHub actions yaml files, do not worry, there is an amazing open-source tool created by [Roberto B](https://twitter.com/RmeetsH) that let's you generate your GitHub Actions yaml for your Laravel Application automatically based on your exact needs:

![GitHub Actions configurator for your Laravel Application](https://imgur.com/GnknXuF.png)

You can find the tool here:

[GitHub Actions configurator for your Laravel Application](https://ghygen.hi-folks.dev/)

You can use the UI to select the features that you need and it will automatically built up the YAML file for you!

Make sure to support [Roberto B](https://twitter.com/RmeetsH) by following him on Twitter!

# Conclusion

If you want to learn more about PestPHP, I would recommend going through this crash course here:

[Introducing Pest - An elegant PHP testing framework](https://devdojo.com/course/introducing-pest)

If you like PestPHP make sure to star it on GitHub:

[https://github.com/pestphp/pest](https://github.com/pestphp/pest)