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.
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/
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:
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:
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:
Top comments (9)
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) ...
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.
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...
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!
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...
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).
Very good point. I've seen so many existing projects without a single test. It is always a headache to take over such projects.
Great Article.
My suggestionis is to download github.com/Hi-Folks/gh-actions-yam...
and start with
take a look to this tool :)
Thanks great article, there is a way to reverse the commit if the tests does not pass?