Laravel Course -Translating a PHPUnit test to Pest
Now we are going to do the test for the post that is the one we have here, you already know that it is a lot of copy and paste, you already know that in case you are not interested in seeing it you can see the last 2 minutes of the video where I quickly show the panning if you do not want to see the process but I am going to do it here so as not to do so much presentation but also to do some development and then behind the scenes I do the rest of the crud operations that we have here so well that is why I do it then a bit the same here you can duplicate the test in PHPUnit since there will be almost no changes as we will see.
I'm going to duplicate it because this way I take advantage of the method where before that we created or well that I created before so I duplicate it here control c control B Here also the good thing about the p ones is that we don't have the blessed nes space I put post nor do we define class names here so well Here I have this I think I'm going to replace it little by little I'm going to close this one this would be the one from php unit I'll see if it gets fixed here and I closed this one well I'm left here one next to the other.
The next thing we have to review is the imports… We shouldn’t change anything, no lie if we change something that would be all this per post according to what we put here and we assign the permissions And from here we start to perform the tests…
This should be all I run here quickly and well they went to the first one I close this at once as always I like to cause an error to see that it is really taking the files and everything works perfectly so well This is also easier now because now we have the tests more organized remember that at the beginning we were testing a lot with the the response that we defined the variable then Here we already started to optimize the code a little therefore already at this point in my opinion I have nothing to optimize, defined when it is needed for example in this case and when it is not needed everything is placed here pasted for the rest it is exactly the same so well nothing without more to say already at this point I am going to do the rest of the crud since well you could see the complete process there is no important change the only important change is obviously the difference in syntax here with the it method and so on or well here the test and the before each that does the replacement of the Setup for the rest everything is as you saw and showed Exactly the same so again I am going to complete the rest of the grot and we will deal with it again in the next module which would be the web module and with that we would finally finish this section so well nothing Thanks for watching and let's go to the next class.
You can see the test code in PHPUnit:
<?php
namespace Tests\Feature\dashboard;
use App\Models\Category;
use App\Models\Post;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
use Tests\TestCase;
class PostTest extends TestCase
{
use DatabaseMigrations;
public User $user;
protected function setUp(): void
{
parent::setUp();
User::factory(1)->create();
$this->user = User::first();
$role = Role::firstOrCreate(['name' => 'Admin']);
Permission::firstOrCreate(['name' => 'editor.post.index']);
Permission::firstOrCreate(['name' => 'editor.post.create']);
Permission::firstOrCreate(['name' => 'editor.post.update']);
Permission::firstOrCreate(['name' => 'editor.post.destroy']);
$role->syncPermissions([1, 2, 3, 4]);
$this->user->assignRole($role);
$this->actingAs($this->user);
}
function test_index()
{
User::factory(1)->create();
$user = User::first();
$this->actingAs($user);
Category::factory(3)->create();
User::factory(3)->create();
Post::factory(20)->create();
$response = $this->get(route('post.index'))
->assertOk()
->assertViewIs('dashboard.post.index')
->assertSee('Dashboard')
->assertSee('Create')
->assertSee('Show')
->assertSee('Delete')
->assertSee('Edit')
->assertSee('Id')
->assertSee('Title')
// ->assertViewHas('posts', Post::paginate(10))
;
$this->assertInstanceOf(LengthAwarePaginator::class, $response->viewData('posts'));
}
function test_create_get()
{
Category::factory(10)->create();
$response = $this->get(route('post.create'))
->assertOk()
->assertSee('Dashboard')
->assertSee('Title')
->assertSee('Slug')
->assertSee('Content')
->assertSee('Category')
->assertSee('Description')
->assertSee('Posted')
->assertSee('Send')
->assertViewHas('categories', Category::pluck('id', 'title'))
->assertViewHas('post', new Post());
$this->assertInstanceOf(Post::class, $response->viewData('post'));
$this->assertInstanceOf(Collection::class, $response->viewData('categories'));
}
function test_create_post()
{
Category::factory(1)->create();
$data = [
'title' => 'Title',
'slug' => 'title',
'content' => 'Content',
'description' => 'Content',
'category_id' => 1,
'posted' => 'yes',
'user_id' => $this->user->id
];
$this->post(route('post.store', $data))
->assertRedirect(route('post.index'));
$this->assertDatabaseHas('posts', $data);
}
function test_create_post_invalid()
{
Category::factory(1)->create();
$data = [
'title' => '',
'slug' => '',
'content' => '',
'description' => '',
// 'category_id' => 1,
'posted' => '',
];
$this->post(route('post.store', $data))
->assertRedirect('/')
->assertSessionHasErrors([
'title' => 'The title field is required.',
'slug' => 'The slug field is required.',
'content' => 'The content field is required.',
'description' => 'The description field is required.',
'posted' => 'The posted field is required.',
'category_id' => 'The category id field is required.',
]);
}
function test_edit_get()
{
User::factory(3)->create();
Category::factory(10)->create();
Post::factory(1)->create();
$post = Post::first();
$response = $this->get(route('post.edit', $post))
->assertOk()
->assertSee('Dashboard')
->assertSee('Title')
->assertSee('Slug')
->assertSee('Content')
->assertSee('Category')
->assertSee('Description')
->assertSee('Posted')
->assertSee('Send')
->assertSee($post->title)
->assertSee($post->content)
->assertSee($post->description)
->assertSee($post->slug)
->assertViewHas('categories', Category::pluck('id', 'title'))
->assertViewHas('post', $post);
$this->assertInstanceOf(Post::class, $response->viewData('post'));
$this->assertInstanceOf(Collection::class, $response->viewData('categories'));
}
function test_edit_put()
{
User::factory(3)->create();
Category::factory(10)->create();
Post::factory(1)->create();
$post = Post::first();
$data = [
'title' => 'Title',
'slug' => 'title',
'content' => 'Content',
'description' => 'Content',
'category_id' => 1,
'posted' => 'yes'
];
$this->put(route('post.update', $post), $data)
->assertRedirect(route('post.index'));
$this->assertDatabaseHas('posts', $data);
$this->assertDatabaseMissing('posts', $post->toArray());
}
function test_edit_put_invalid()
{
User::factory(3)->create();
Category::factory(10)->create();
Post::factory(1)->create();
$post = Post::first();
$this->get(route('post.edit', $post));
$data = [
'title' => 'a',
'slug' => '',
'content' => '',
'description' => '',
// 'category_id' => 1,
'posted' => '',
];
$this->put(route('post.update', $post), $data)
->assertRedirect(route('post.edit', $post))
->assertSessionHasErrors([
'title' => 'The title field must be at least 5 characters.',
'slug' => 'The slug field is required.',
'content' => 'The content field is required.',
'description' => 'The description field is required.',
'posted' => 'The posted field is required.',
'category_id' => 'The category id field is required.',
])
;
}
function test_edit_destroy()
{
User::factory(3)->create();
Category::factory(10)->create();
Post::factory(1)->create();
$post = Post::first();
$data = [
'id' => $post->id
];
$this->delete(route('post.destroy', $post))
->assertRedirect(route('post.index'));
$this->assertDatabaseMissing('posts', $data);
}
}
And with Pest:
<?php
use App\Models\User;
use App\Models\Post;
use App\Models\Category;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
beforeEach(function () {
User::factory(1)->create();
$this->user = User::first();
$role = Role::firstOrCreate(['name' => 'Admin']);
Permission::firstOrCreate(['name' => 'editor.post.index']);
Permission::firstOrCreate(['name' => 'editor.post.create']);
Permission::firstOrCreate(['name' => 'editor.post.update']);
Permission::firstOrCreate(['name' => 'editor.post.destroy']);
$role->syncPermissions([1, 2, 3, 4]);
$this->user->assignRole($role);
$this->actingAs($this->user);
});
test('test index', function () {
Category::factory(3)->create();
User::factory(3)->create();
Post::factory(20)->create();
$response = $this->get(route('post.index'))
->assertOk()
->assertViewIs('dashboard.post.index')
->assertSee('Dashboard')
->assertSee('Create')
->assertSee('Show')
->assertSee('Delete')
->assertSee('Edit')
->assertSee('Id')
->assertSee('Title')
// ->assertViewHas('posts', Post::paginate(10))
;
$this->assertInstanceOf(LengthAwarePaginator::class, $response->viewData('posts'));
});
test('test create get', function () {
Category::factory(10)->create();
$response = $this->get(route('post.create'))
->assertOk()
->assertSee('Dashboard')
->assertSee('Title')
->assertSee('Slug')
->assertSee('Content')
->assertSee('Category')
->assertSee('Description')
->assertSee('Posted')
->assertSee('Send')
->assertViewHas('categories', Category::pluck('id', 'title'))
->assertViewHas('post', new Post());
$this->assertInstanceOf(Post::class, $response->viewData('post'));
$this->assertInstanceOf(Collection::class, $response->viewData('categories'));
});
test('test create post', function () {
Category::factory(1)->create();
$data = [
'title' => 'Title',
'slug' => 'title',
'content' => 'Content',
'description' => 'Content',
'category_id' => 1,
'posted' => 'yes',
'user_id' => $this->user->id
];
$this->post(route('post.store', $data))
->assertRedirect(route('post.index'));
$this->assertDatabaseHas('posts', $data);
});
test('test create post invalid', function () {
Category::factory(1)->create();
$data = [
'title' => '',
'slug' => '',
'content' => '',
'description' => '',
// 'category_id' => 1,
'posted' => '',
];
$this->post(route('post.store', $data))
->assertRedirect('/')
->assertSessionHasErrors([
'title' => 'The title field is required.',
'slug' => 'The slug field is required.',
'content' => 'The content field is required.',
'description' => 'The description field is required.',
'posted' => 'The posted field is required.',
'category_id' => 'The category id field is required.',
]);
});
test('test edit get', function () {
User::factory(3)->create();
Category::factory(10)->create();
Post::factory(1)->create();
$post = Post::first();
$response = $this->get(route('post.edit', $post))
->assertOk()
->assertSee('Dashboard')
->assertSee('Title')
->assertSee('Slug')
->assertSee('Content')
->assertSee('Category')
->assertSee('Description')
->assertSee('Posted')
->assertSee('Send')
->assertSee($post->title)
->assertSee($post->content)
->assertSee($post->description)
->assertSee($post->slug)
->assertViewHas('categories', Category::pluck('id', 'title'))
->assertViewHas('post', $post);
$this->assertInstanceOf(Post::class, $response->viewData('post'));
$this->assertInstanceOf(Collection::class, $response->viewData('categories'));
});
test('test edit put', function () {
User::factory(3)->create();
Category::factory(10)->create();
Post::factory(1)->create();
$post = Post::first();
$data = [
'title' => 'Title',
'slug' => 'title',
'content' => 'Content',
'description' => 'Content',
'category_id' => 1,
'posted' => 'yes'
];
$this->put(route('post.update', $post), $data)
->assertRedirect(route('post.index'));
$this->assertDatabaseHas('posts', $data);
$this->assertDatabaseMissing('posts', $post->toArray());
});
test('test edit put invalid', function () {
User::factory(3)->create();
Category::factory(10)->create();
Post::factory(1)->create();
$post = Post::first();
$this->get(route('post.edit', $post));
$data = [
'title' => 'a',
'slug' => '',
'content' => '',
'description' => '',
// 'category_id' => 1,
'posted' => '',
];
$this->put(route('post.update', $post), $data)
->assertRedirect(route('post.edit', $post))
->assertSessionHasErrors([
'title' => 'The title field must be at least 5 characters.',
'slug' => 'The slug field is required.',
'content' => 'The content field is required.',
'description' => 'The description field is required.',
'posted' => 'The posted field is required.',
'category_id' => 'The category id field is required.',
])
;
});
test('test destroy', function () {
User::factory(3)->create();
Category::factory(10)->create();
Post::factory(1)->create();
$post = Post::first();
$data = [
'id' => $post->id
];
$this->delete(route('post.destroy', $post))
->assertRedirect(route('post.index'));
$this->assertDatabaseMissing('posts', $data);
});
Rest of the tests:
https://github.com/libredesarrollo/book-course-laravel-base-11
- Andrés Cruz
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.
Develop with Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter