DEV Community

Cover image for How to Automatically Run Your Laravel PestPHP Tests on Each GitHub Pull Request?
Bobby Iliev
Bobby Iliev

Posted on • Originally published at devdojo.com

How to Automatically Run Your Laravel PestPHP Tests on Each GitHub Pull Request?

Introduction

PestPHP was created by Nuno Maduro 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

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 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:

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

Introductin to GitHub Actions

GitHub 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:

Automatically Run your PestPHP tests

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

mkdir .github/workflows/
Enter fullscreen mode Exit fullscreen mode

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

touch .github/workflows/tests.yaml
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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/
Enter fullscreen mode Exit fullscreen mode

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

touch .github/workflows/tests.yaml
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

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 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

You can find the tool here:

GitHub Actions configurator for your Laravel Application

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 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

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

https://github.com/pestphp/pest

Discussion (9)

Collapse
leob profile image
leob • Edited

First time I hear about this testing framework, it looks inspired by BDD (Behavior Driven Development), Mocha/Jasmine etc ... admittedly the syntax is slick and terse, but TBH I don't see that as a sufficiently compelling reason to switch from trusty Phpunit to this new thing (especially when one is already heavily invested in Phpunit, and given the fact that the Laravel testing docs are based on Phpunit) ...

Collapse
bobbyiliev profile image
Bobby Iliev Author

I personally love it and use it for all new projects. The framework is quite new, but having in mind that the creator is a Laravel core member I would not be surprised to see it in the docs soon. It has been added as an option to Jetstream and Breeze already.

Collapse
bobbyiliev profile image
Bobby Iliev Author

Inspired by your comment, I started a discussion on Twitter to gather some opinions on why people might want to switch to PestPHP. Feel free to join the discussion!

twitter.com/bobbyiliev_/status/143...

Thread Thread
leob profile image
leob

I'd be inclined to use it on new (greenfield) projects, not to go and rewrite my existing tests ... but, I can't really offer an educated opinion on the pros and cons as I simply don't have any experience with this new framework!

Thread Thread
leob profile image
leob

Hey what about Laravel Dusk (browser based testing), can that also be driven thru Pest rather than thru Phpunit? Ah here's the answer already: pestphp.com/docs/plugins/laravel#l...

Collapse
leob profile image
leob

I recognize the style/syntax from the JS world, it's a BDD approach ... I'm not against it if it's an extra option in addition to phpunit, and people can choose what they like, more choices is not necessarily a bad thing.

For me the important thing to do is to always use TDD (or BDD) on projects, it has so many advantages (and with "use TDD" I mean use it right from the start, to drive the design of the codebase, not as an afterthought).

Thread Thread
bobbyiliev profile image
Bobby Iliev Author

Very good point. I've seen so many existing projects without a single test. It is always a headache to take over such projects.

Collapse
robertobutti profile image
Roberto B.

Great Article.
My suggestionis is to download github.com/Hi-Folks/gh-actions-yam...
and start with

php artisan ghygen:generate
Enter fullscreen mode Exit fullscreen mode

take a look to this tool :)

Collapse
arielmejiadev profile image
Ariel Mejia

Thanks great article, there is a way to reverse the commit if the tests does not pass?