Creating Plugins
Overview
Plugins can extend Pest's functionality and are easy to write.
Setting up the plugin template
To create your own plugin, it's easiest to start with the pest-plugin-template.
To create a new repository from it click on the "Use this template" button. Call it pest-plugin-<yourpluginname>
.
Now clone the new repository to work on your plugin.
Edit composer.json and adjust the fields name
and description
to fit your plugin.
Adding uses
Methods can also be added for the $this
variable inside closures, or to higher order tests.
We are going to add a trait and tell Pest to use it. We are going to call our imaginary plugin MyPlugin
.
For writing your own plugin, choose your own name and make sure you adjust all occurrences in the following code.
First, let's create our trait directly in src/MyPluginTrait.php
:
1<?php 2 3namespace YourGithubUsername\PestPluginName; 4 5trait MyPluginTrait 6{ 7 public function visitHomePage() 8 { 9 return $this->get('/')10 ->assertSee('Welcome');11 }12 13 public function actOnProfilePage()14 {15 $user = factory(User::class)->create();16 17 return $this->actingAs($user)->get('/profile')->assertSee($user->name);18 }19}
Make sure, you always return the object, so high order functionality isn't broken.
This will allow us to write tests like this:
1it('has a homepage')2 ->visitHomePage()3 ->actOnProfilePage()4 ->assertTrue(true);
To make the functionality available, we need to let Pest know that it should use it. We will do this in Autoload.php
:
1use YourGithubUsername\PestPluginName\MyPluginTrait;2 3Pest\Plugin::uses(MyPluginTrait::class);
And finally, we have to load the files. We adjust our composer.json
as following, to reference our namespace
for PSR-4 autoloading and tell it to load and run our Plugin.php
:
1...2"autoload": {3 "psr-4": {4 "YourGithubUsername\\PestPluginName\\": "src/"5 },6 "files": ["src/Autoload.php"]7},8...
Adding functions
A plugin may integrate additional functions. These may be even external libraries. A good example is the Faker plugin, which we are going to reproduce here.
If your plugin has any dependencies, then add them first with composer
:
1composer require fzaninotto/faker
All plugin files reside inside the src/
folder. Let's assume we want to provide a faker()
function
for our tests. We create a file Autoload.php
with the following code:
1<?php 2 3namespace YourGithubUsername\PestPluginName; 4 5use Faker\Factory; 6use Faker\Generator; 7 8function faker(string $locale = null): Generator 9{10 return Factory::create($locale ?? Factory::DEFAULT_LOCALE);11}
Inside functions, to access the current test case in your plugin, you can use the global function
test()
.
This provides a faker()
function inside our tests, by creating an object and returning it.
Now we can use Faker
directly and easily in our tests:
1use function YourGithubUsername\PestPluginName\faker;2 3it('doing something with faker', function() ) {4 assertIsString(faker()->name);5}
Before our faker()
function is available though, we must ensure it is always loaded.
To load our extension, we must add or adjust the autoload
section in the composer.json
to point to the files we want to load:
1...2 "autoload": {3 "files": ["src/Autoload.php"]4 },5...
Any amount of further files can be added this way. They will be loaded automatically.
Adding expectations
A plugin may integrate additional expectations. For example, let's say that you're providing a plugin that adds the toBeWithinRange
expectation.
First, you would create a file Autoload.php
with the following code:
1<?php2 3expect()->extend('toBeWithinRange', function ($min, $max) {4 return $this->toBeGreaterThanOrEqual($min)5 ->toBeLessThanOrEqual($max);6});
In expectation closures, the
$this
variable is also available to give you access the expectation value.
Finally, to load our extension, we must add or adjust the autoload
section in the composer.json
to point to the files we want to load:
1...2 "autoload": {3 "files": ["src/Autoload.php"]4 },5...
Any amount of further files can be added this way. They will be loaded automatically.
Next section: Changelog →