2 Comments

How to convert PHPunit to Pest PHP

Pest is a new but great PHP testing framework. It has a simple and elegant syntax and a few nice features build on top of PHPunit. In this article, I'll show you how to convert a PHPunit test suite to Pest.

Installing Pest

Require two composer packages and run the install command:

composer require pestphp/pest --dev --with-all-dependencies
composer require pestphp/pest-plugin-laravel --dev
php artisan pest:install

That's it! Now Pest is installed in your Laravel project!

Converting a test

I will use this test as an example:

<?php
  
// tests/Feature/SeePostsTest.php
  
namespace Tests\Feature;

use App\Models\Post;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class SeePostsTest extends TestCase
{
    use RefreshDatabase;
  
    /** @test */
    public function a_user_can_see_all_posts()
	{
	    $this->post(route('posts.index'))
		    ->assertStatus(200)
		    ->assertSee($this->post->title);
	}
  
    /**
	 * @dataProvider titleProvider
	 * @test
	 */
    public function it_is_a_title($title)
	{
	    $this->assertIsString($title);
	}
  
    public titleProvider()
	{
	    return [
		    'A post title',
		    'Another title',
		    'And another...'
		];
	}
}

Now you can convert this to Pest by using the test() function instead of classes.

<?php
  
namespace Tests\Feature;
  
// tests/Feature/SeePostsTest.php

use App\Models\Post;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

test('a user can see all posts', function() {
    $post = Post::factory()->create();
	  
	$this->post(route('posts.index'))
	     ->assertStatus(200)
	     ->assertSee($post->title);
});

test('it is a title', function() {
    $this->assertIsString($title);
})->with([
    'A post title',
    'Another title',
    'And another...'
]);

Note that I used Pest's datasets in the second test.

Because we removed the RefreshDatabase trait, we'll have to add it again. But using Pest, you can do this in the Pest.php file, so you don't have to add it for every test.

// tests/Pest.php

<?php

uses(Tests\TestCase::class)->in('Feature');
uses(Illuminate\Foundation\Testing\RefreshDatabase::class)->in('Feature');

Now we can remove the namespace and a few imports from our test.

<?php
  
namespace Tests\Feature;
  
// tests/Feature/SeePostsTest.php

use App\Models\Post;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

test('a user can see all posts', function() {
    $post = Post::factory()->create();
	  
	$this->post(route('posts.index'))
	     ->assertStatus(200)
	     ->assertSee($post->title);
});

test('it is a title', function() {
    $this->assertIsString($title);
})->with([
    'A post title',
    'Another title',
    'And another...'
]);

Another nice thing that we can do with Pest, is use the it() function instead of test(). It is a small addition, but nontherless great.

<?php
  
// tests/Feature/SeePostsTest.php

use App\Models\Post;

test('a user can see all posts', function() {
    $post = Post::factory()->create();
	  
	$this->post(route('posts.index'))
	     ->assertStatus(200)
	     ->assertSee($post->title);
});

test('it is a title', function() {
it('is a title', function() {
    $this->assertIsString($title);
})->with([
    'A post title',
    'Another title',
    'And another...'
]);

The expectation API

Pest has a really nice expectation API that makes your tests a lot more readable. They almost become really English sentences.

It works by calling the expect() function and chaining expectations on top of it. Here are a few examples:

expect($title)->toBe('My Post Title');
expect($posts)->toHaveCount(3);
expect($id)->not->toBe(14);
expect($description)->toStartWith('This post is about')->and($description)->toEndWith('Pest PHP.');

You can use the expectation API everywhere where you call $this and then some sort of assertion. In the case of the test above, we can use it in the second test.

<?php
  
// tests/Feature/SeePostsTest.php

use App\Models\Post;

test('a user can see all posts', function() {
    $post = Post::factory()->create();
	  
	$this->post(route('posts.index'))
	     ->assertStatus(200)
	     ->assertSee($post->title);
});

it('is a title', function() {
    $this->assertIsString($title);
    expect($title)->toBeString();
})->with([
    'A post title',
    'Another title',
    'And another...'
]);

You can see all available expectations at the Pest PHP documentation.

Conclusion

Pest PHP is a relatively young, but very nice PHP testing framework. It's very easy to convert PHPunit tests to Pest PHP. If you want to learn more about Pest, check out the official documentation.

Share this article:

Subscribe to my newsletter

Continue reading:

Eloquent polymorphic relationships explained (with examples)

In my opinion, Eloquent is one of the most powerful features of Laravel. It is an API for interacting with your database, and it has a very nice and easy-to...

Eloquent relationships explained (with examples)

In my opinion, Eloquent is one of the most powerful features of Laravel. It is an API for interacting with your database, and it has a very nice and easy-to...

The TALL stack explained

The TALL stack is a set of frameworks to build interactive apps using Laravel. It stands for Tailwind CSS, Alpine.JS, Laravel, and Livewire. In this article,...

Leave a comment

Comments (2)

    monaye's avatar
    monaye Reply
    1 month ago

    just fyi, you have a bug in your "Continue reading:" section. excerpt showing in the section are all from the main blog post.

      Jeroen van Rensen's avatar
      Jeroen van Rensen
      1 month ago

      Thanks, now it's fixed!