This blog post is Human-Centered Content: Written by humans for humans.
If you’ve created a Laravel app before you may be used to using tools like Pest or PHPStan. These are great for testing the functionality of the app and readability of your code. However, these both require the structure of a Laravel app to run and our repo only contains the package code. Fortunately, a tool called Testbench has been created for exactly this!
You can install Testbench with:
composer require --dev "orchestra/testbench"
There a few more setup steps, depending on the testing framework you’d like to use. I’ll go through what I’ve done to set Pest up but there are heaps of resources out there than can help you get other frameworks up and running.
Setting Up Pest
First, of course, you need to install Pest:
composer remove phpunit/phpunit
If you run it as is, it will be sad about any Laravel classes you’re using within your package. The namespaces are built as if the package will be loaded in the vendor directory of an app, but right now it’s on its own in the packages directory. To fix this, we need to create a custom TestCase.php that pulls in Testbench. Here’s my entire file, located at packages/interworks/thoughtspotrest/tests/TestCase.php:
<?php namespace InterWorks\ThoughtSpotREST\Tests; use Illuminate\Contracts\Config\Repository; use InterWorks\ThoughtSpotREST\ThoughtSpotRESTServiceProvider; use Orchestra\Testbench\TestCase as Orchestra; abstract class TestCase extends Orchestra { protected function defineEnvironment($app) { // Setup default database to use sqlite :memory: tap($app['config'], function (Repository $config) { $config->set('thoughtspotrest.auth', 'basic'); $config->set('thoughtspotrest.password', 'password'); $config->set('thoughtspotrest.url', 'https://example.thoughtspot.com'); $config->set('thoughtspotrest.username', 'username'); }); } protected function getPackageProviders($app) { return [ ThoughtSpotRESTServiceProvider::class, ]; } }
Our files will probably look different depending on the dependencies. If your package doesn’t have config values or a service provider, you can leave it empty between the curly brackets. This package requires the config values to function or an exception will be thrown, and the defineEnvironment function allows me to spoof those values. My Pest tests will mock REST API responses as opposed to trying to actually reach out to ThoughtSpot, so the values don’t matter as long as they’re set.
Next, we have to tell Pest to use this custom TestCase.php in the Pest.php file, located at packages/interworks/thoughtspotrest/tests/Pest.php:
<?php use InterWorks\ThoughtSpotREST\Tests\TestCase; uses(TestCase::class)->in(__DIR__);
Those three lines are the entire file. I’m just pointing to the TestCase class I created leveraging Pest’s uses() function.
And that’s it! Pest is now set up to run in my package’s repo. Running it locally is great, but open source users are going to want some proof this thing is being tested. And how we prove that is running our tests directly in GitHub.
Running Tests in GitHub
GitHub makes running tests so simple. If you have the requisite files, GitHub will detect the tests and run them automatically for you for free. It’s worth noting there are limits, but they’re tough to hit unless your package has a high amount of pull requests against it. You can have your actions run against your own infrastructure or pay for high usage if your package explodes in popularity.
GitHub looks for any YAML files in the .github/workflows/ directory. You can run more than one test in a single YAML file or break them up into multiple. Each file will be its own action run. You can embed the result of the latest action run per file in your README. You may have noticed a little icon that says “tests” on some of the open-source packages you’ve used. That package likely has all their tests in one file which would be one action. I’ve opted to have them in multiple so I can show my package is passing both Pest and PHPStan. The only other difference between one verses multiple files is multiple files can run in parallel and possibly be a little quicker.
I have a .github/workflows/pest.yml file and a .github/workflows/phpstan.yml file to define my two test actions. Below is my pest.yml file:
name: Pest on: push: branches: - main pull_request: branches: - main jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up PHP uses: shivammathur/setup-php@v2 with: php-version: '8.2' extensions: mbstring, pdo, json, bcmath, ctype - name: Clear Composer Cache run: composer clear-cache - name: Install Dependencies run: composer install --prefer-dist --no-interaction --optimize-autoloader - name: Run Tests run: vendor/bin/pest
Let’s break down each piece:
- “name” – The name used to identify the action on GitHub.
- “on” – This section tells GitHub how often to run this action. In this case, it should run on any pushes and PR’s to the main branch.
- “jobs” – The stuff this action should do:
- “runs-on” – The image to use.
- “steps” – The steps for the action to take. The following will occur:
- Install PHP 8.2 with the necessary extensions.
- Clear the composer cache.
- Install the composer dependencies.
- Run Pest.