Unit Testing with Pest vs PHPUnit

As a demonstration, we are going to do the translation of the test for the php unit posts to pest together. If you don't want to see it, you can go to the end of the video, the last minute or 2 minutes, and see how the test looks or go directly to the repository as I told you. In other words, you could perfectly skip this class. So if you stayed, it's because you want to see the translation. Obviously, I'm going to copy and paste to have the whole structure here and take advantage of and copy the methods.

So, the first thing I'm going to do here is copy a little bit of the content of the test created with PHPUnit here, to have it ready here. This is a lot of copy and paste. Remember that I've already told you this here. I'm going to remove everything that I'm not going to need. In this case, I think that these two are the user ones too, to see if the user is also good, although I'm not lying...
Let's go with the next one. I'm going to clone it, give it a space. It would be this good test get by ID. I'm going to cut all of this up to here. I'll place it. You can see that everything is practically the same. I cut it here. You could leave it like I told you. But I prefer to have it all on a single line because again we're not doing anything with the response. So logically we can simplify it. I remove this, I remove the reference, and this is the protocol that you have to follow until you finish. So, we're going to continue until you finish this. Yes, I'm going to copy the name. Oh, I won't put it. 404 will be for the errors part. It would be here, here, here. We already have one with the heer. We'll have to see if here when we do it with pest it requires it in This case did require it, if it was not sent, I don't remember if it was on another page, but I'm going to define it there anyway and pending on deleting this, it would remain like this as a Post that does not exist and we evaluate the response here, I also duplicate it here:

test('test all', function () {

    Category::factory(10);
    $categories = Category::get()->toArray();

    $this->get(
        '/api/category/all',
        [
            'Authorization' => 'Bearer ' . generateTokenAuth()
        ]
    )->assertOk()->assertJson($categories);
});

In essence, we must remove the assertHeader and place it as the third parameter here. This was a way to do it if you could not go directly to the assertion method with the database, the missing one or if it is defined as we saw later:

$this->post('/api/category', $data, [
            'Accept' => 'application/json',
            'Authorization' => 'Bearer ' . $this->generateTokenAuth()
        ])

To replace the assertion method assertStringContainsString that does not exist in Pest, we use assertMatchesRegularExpression that works by creating regular expressions:

assertMatchesRegularExpression("/The title field is required./", $response->getContent());

… We continue a little remove this space enter here I remove this one here I put put here a little the same, it should be we paste the Data … the header and the third parameter and we evaluate the response here we continue here short we move the header … finally the last test delete only test delete I paste here we are going to remove the respons we do not pass this one:

it("test delete auth", function () {
    Category::factory(1)->create();
    $category = Category::first();

    $this->delete('/api/category/' . $category->id,[], [
        'Accept' => 'application/json',
        'Authorization' => 'Bearer ' . $this->generateTokenAuth()
    ])->assertStatus(200)
        ->assertContent('"ok"');

    $category = Category::find($category->id);
    $this->assertEquals($category, null);
});

And there is no problem with that posts posts Ah, I repeated one of the tests to see twice, there is one that I left here, it is also here, look…

Summary

I hope it has been useful to you, in the same way I show it to you if you got here now I am going to show you a little bit of the code is as I presented to you before here as I told you at the beginning as I have told you in the previous reviews I removed the response when it is not necessary since we left it there as a dead variable and for the rest we moved the header and the assertion method of making regular expression matches to evaluate here the regular expression that is a Stream there directly that we are passing it for the rest Everything remains exactly the same so with this we finish the tests for the post and the user test is missing which I already did as you can see and I show it to you in the following video.

The tests with Pest do not change much from those implemented with PHPUnit, it is a slightly simpler syntax and below are the tests translated previously with Pest:

tests/Feature/Api/CategoryTest.php

<?php
use App\Models\Category;

test('test all', function () {

    Category::factory(10);
    $categories = Category::get()->toArray();

    $this->get(
        '/api/category/all',
        [
            'Authorization' => 'Bearer ' . generateTokenAuth()
        ]
    )->assertOk()->assertJson($categories);
});

it("test get by id", function () {
    Category::factory(1)->create();
    $category = Category::first();

    $this->get('/api/category/' . $category->id, [
        'Authorization' => 'Bearer ' . $this->generateTokenAuth()
    ])->assertStatus(200)->assertJson([
                'id' => $category->id,
                'title' => $category->title,
                'slug' => $category->slug
            ]);
});
it("test get by slug", function () {
    Category::factory(1)->create();
    $category = Category::first();

    $this->get('/api/category/slug/' . $category->slug, [
        'Authorization' => 'Bearer ' . $this->generateTokenAuth()
    ])->assertStatus(200)->assertJson([
                'id' => $category->id,
                'title' => $category->title,
                'slug' => $category->slug
            ]);
});
it("test post", function () {
    $data = ['title' => 'Cate 1', 'slug' => 'cate-2-new'];
    $this->post('/api/category', $data, [
        'Accept' => 'application/json',
        'Authorization' => 'Bearer ' . $this->generateTokenAuth()
    ])->assertStatus(200)->assertJson($data);
});
it("test post error form title", function () {
    $data = ['title' => '', 'slug' => 'cate-2-new'];
    $response = $this->post('/api/category', $data, [
        'Accept' => 'application/json',
        'Authorization' => 'Bearer ' . $this->generateTokenAuth()
    ])->assertStatus(422);

    // $this->assertStringContainsString("The title field is required.", $response->getContent());
    $this->assertMatchesRegularExpression("/The title field is required./", $response->getContent());
    // $testArray = array("a"=>"value a", "b"=>"value b"); 
    // $value = "value b";  
    // // assert function to test whether 'value' is a value of array 
    // $this->assertContains($value, $testArray) ;

    // $this->assertContains("The title field is required.",['title'=>'["The title field is required."]']);
});
it("test post error form slug", function () {
    $data = ['title' => 'cate 3', 'slug' => ''];
    $response = $this->post('/api/category', $data, [
            'Accept' => 'application/json',
            'Authorization' => 'Bearer ' . $this->generateTokenAuth()
        ])->assertStatus(422);

    // $response->assertStringContainsString("The slug field is required.", $response->getContent());
    $this->assertMatchesRegularExpression("/The slug field is required./", $response->getContent());
});
it("test post error form slug unique", function () {
    Category::create(
        [
            'title' => 'category title',
            'slug' => 'cate-3'
        ]
    );

    $data = ['title' => 'cate 3', 'slug' => 'cate-3'];

    $response = $this->post('/api/category', $data, [
        'Accept' => 'application/json',
        'Authorization' => 'Bearer ' . $this->generateTokenAuth()
    ])->assertStatus(422);

    $this->assertStringContainsString("The slug has already been taken.", $response->getContent());
});

it("test get by id 404", function () {
    $this->get('/api/category/1000', [
        'Accept' => 'application/json',
        'Authorization' => 'Bearer ' . $this->generateTokenAuth()
    ])->assertStatus(404)->assertContent('"Not found"');

});
it("test get by slug 404", function () {
    $this->get('/api/category/slug/cate-not-exist', [
        'Accept' => 'application/json',
    ])->assertStatus(404)->assertContent('"Not found"');

});

it("test put", function () {
    Category::factory(1)->create();
    $categoryOld = Category::first();

    $dataEdit = ['title' => 'Cate new 1', 'slug' => 'cate-1-new'];

    $this->put('/api/category/' . $categoryOld->id, $dataEdit, [
        'Accept' => 'application/json',
        'Authorization' => 'Bearer ' . $this->generateTokenAuth()
    ])->assertStatus(200)->assertJson($dataEdit);
});

it("test delete auth", function () {
    Category::factory(1)->create();
    $category = Category::first();

    $this->delete('/api/category/' . $category->id,[], [
        'Accept' => 'application/json',
        'Authorization' => 'Bearer ' . $this->generateTokenAuth()
    ])->assertStatus(200)
        ->assertContent('"ok"');

    $category = Category::find($category->id);
    $this->assertEquals($category, null);
});

Since these are resources protected by authentication, we define the method to generate the token within the Pest class:

tests/Pest.php

uses(TestCase::class, RefreshDatabase::class)->in('Feature');
function generateTokenAuth()
{
    User::factory()->create();
    return User::first()->createToken('myapptoken')->plainTextToken;
}

In the above file, you can also see that they have the RefreshDatabase trait that we used earlier with PHPUnit.

- Andrés Cruz

En español

This material is part of my complete course and book; You can purchase them from the books and/or courses section, Curso y Libro Laravel 11 con Tailwind Vue 3, introducción a Jetstream Livewire e Inerta desde cero - 2024.

Andrés Cruz

Develop with Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz In Udemy

I agree to receive announcements of interest about this Blog.

!Courses from!

10$

On Udemy

There are 1d 21:33!


Udemy

!Courses from!

4$

In Academy

View courses

!Books from!

1$

See the books
¡Become an affiliate on Gumroad!