Quay lại

Cách sử dụng Pest để viết unit test trong Laravel - (2023) Chuyên mục PHP và Laravel    2023-05-14    75 Lượt xem    22 Lượt thích    comment-3 Created with Sketch Beta. 0 Bình luận

Cách sử dụng Pest  để viết unit test trong Laravel - (2023)

Kiểm thử là một kỹ năng quan trọng đối với mọi developer. Khi xây dựng các ứng dụng, ban đầu có thể cảm thấy hơi lãng phí thời gian, nhưng mình có thể nói từ kinh nghiệm rằng nó mang lại rất nhiều sự an tâm sau này. Trong bài viết này mình sẽ chỉ cho bạn cách làm thế nào để viết 1 unit test sử dụng framework Pest test.

Hầu hết mọi người và các framework vẫn sử dụng PHPUnit, đây là một framework testing tuyệt vời. Hiện tại, chúng ta cũng đang chứng kiến ​​sự chuyển đổi rộng rãi hơn sang việc sử dụng Pest. Pest cung cấp một cách test tốt và trôi chảy hơn, vì vậy đây chắc chắn là một lựa chọn tốt. Trong bài viết này, mình sẽ chỉ cho bạn kiến ​​thức cơ bản về Pest để bạn có thể bắt đầu sử dụng nó một cách nhanh chóng.

Học Pest không khó. Cú pháp hơi khác một chút so với PHPUnit, nhưng cách test hầu như giống nhau.

Cài đặt

Nếu bạn nào sử dụng laravel phiên bản 11 thì Pest đã được caid mặc định, còn dùng phiên bản cũ hơn thì tham khảo config dứoi đây nhé!

Việc cài đặt Pest rất đơn giản. Chạy các lệnh sau để cài đặt và cấu hình Pest:

Như mình có đọc docs tại thời điểm hiện tại là pestphp/pest là v2.6.1 và yêu cầu php version từ 8.1 trờ lên

  • php: ^8.1.0
  • brianium/paratest: ^7.1.4
  • nunomaduro/collision: ^7.5.2
  • nunomaduro/termwind: ^1.15.1
  • pestphp/pest-plugin: ^2.0.1
  • pestphp/pest-plugin-arch: ^2.1.2
  • phpunit/phpunit: ^10.1.3

Còn về phiên bản của laravel từ bản 10 trở lên mới cài được Pest version 2, chính vì thế các bạn lưu ý điều này :

Nếu laravel của bạn là version 9 trở xuống thì chỉ cài được phiên bản Pest version 1 mà thôi ! Các bạn cài bằng cách sau:

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

Trường hợp là laravel version 10 trở lên thì chạy lệnh sau

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

Thao tác này sẽ tạo tệp Pest.php trong thư mục tests vat tệp Pest.php sẽ tự động được auto load.

Cách Sử Dụng

Trước khi đi vào cách sử dụng của Pest thì các bạn có thể xem lại 1 example của larvavel đã cung cấp trước đó trong thư mục tests/Feature và mở file ExampleTest.php

<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testBasicTest()
    {
        $response = $this->get('/');
        $response->assertStatus(200);
    }

Như các bạn thấy laravel dựa trên PHPUnit để thực thi test mà k dựa trên bất kỳ framework nào khác, bây giờ chúng ta hãy chuyển sang Pest để thay thế để xem nó hoạt động như thế  nào nhé 

Copy đoạn code dưới đây để thay thế cho đoạn code trên :

<?php

it('has welcome page')->get('/')->assertStatus(200);

Pest thì cung cấp 2 functions để phục vụ cho test phương thức test() và it() cả 2 hàm này đều chấp nhận với 1 discription là đối số đầu tiên và closure chứa test expectations là tham số đầu vào thứ 2, Chúng chia sẻ cùng một cú pháp và hành vi, và bạn có thể tự do sử dụng bất cứ thứ gì bạn thấy phù hợp. Với Cá nhân mình thì mình thích sử dụng it(), chức năng này để đọc các trường hợp thử nghiệm dưới dạng một câu hoàn chỉnh.

Tương tự, hãy tạo tệp trong thư mục tests/Unit và sử dụng Pest. Thay thế nội dung của tệp bằng mã sau: ExampleTest.php

<?php

test('basic')->assertTrue(true);

Để  chạy được test thì các bạn chạy cmd sau :

$ ./vendor/bin/pest

Command trên sẽ chạy tất cả các test case và cho ra kết quả đã passed được show như dưới đây:

Tạo Model

Ứng dụng của chúng ta sẽ có một model duy nhất được gọi là Todo. Laravel cung cấp một lệnh thuận tiện để tạo đồng thời một model, migration và controller cho một thực thể. Để thực hiện việc này, hãy chạy lệnh sau:

$ php artisan make:model Todo -m -c

Tạo  Migration

Một tệp migration mới được tạo trong cơ sở dữ liệu thư mục database/migrations tại [TODAYSDATE]_create_todos_table.php. Sau đó, thêm đoạn mã sau vào phương thức up() trong migration:

 $table->string('name');
 $table->boolean('completed')->default(false);

Mỗi to do-task sẽ có một thuộc tính name, và thuộc tính completed với kiểu boolean với giá trị mặc định là false. Sau đó sửa đổi tệp App/Models/Todo.php với mã sau:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Todo extends Model
{
    use HasFactory;

    protected $fillable = ['name', 'completed'];
}

Ở đây chúng ta gán thuộc tính namecompleted của model để chúng có thể được gán hàng loạt.

Tạo factory

Các model factories của Laravel cung cấp một cách thuận tiện để đưa dữ liệu vào cơ sở dữ liệu. Điều này rất hữu ích khi chạy test. Chạy lệnh sau để tạo factory class cho model to-do:

$ php artisan make:factory TodoFactory

Thao tác này sẽ tạo TodoFactory.php cho chúng ta trong thư mục database/factories. Sửa đổi phương thức definition() để trả về một mảng tương tự như bên dưới:

return [
    'name' => 'Deploy Todo Verify to Live',
    'completed' => false
];

Phương thức trên trả về tập giá trị thuộc tính, sẽ được áp dụng khi tạo model bằng cách sử dụng factory.

Create Controller

Bây giờ bạn hãy vào thư mục app/http/Controller và tạo 1 file là TodoController.php và sau đó sử dụng đoạn code của mình dứoi đây nhé !

<?php

namespace App\Http\Controllers;

use App\Models\Todo;
use Illuminate\Http\Request;

class TodoController extends Controller
{
    public function create(Request $request)
    {
        $request->validate($this->rules());

        $todo = Todo::create($request->only(['name']));

        $data = [
            'message' => 'To-do has been created',
            'todo' => $this->mapTodoResponse($todo)
        ];

        return response()->json($data, 201);
    }

    public function show(Todo $todo)
    {
        $data = [
            'message' => 'Retrieved To-do',
            'todo' => $this->mapTodoResponse($todo)
        ];

        return response()->json($data);
    }

    public function update(Todo $todo, Request $request)
    {
        $request->validate($this->rules());

        $todo->update($request->only(['name']));
        $todo->refresh();

        $data = [
            'message' => 'To-do has been updated',
            'todo' => $this->mapTodoResponse($todo)
        ];

        return response()->json($data);
    }

    public function delete(Todo $todo)
    {
        $todo->delete();

        $data = [
            'message' => 'To-do has been deleted'
        ];

        return response()->json($data);
    }

    protected function rules()
    {
        return [
            'name' => 'required|string|min:4'
        ];
    }

    protected function mapTodoResponse($todo)
    {
        return [
            'id' => $todo->id,
            'name' => $todo->name,
            'completed' => $todo->completed
        ];
    }
}
  • The method create() tạo mới to-do task.
  • The method show() trả về một task dựa vào ID.
  • The method update() cập nhập một to-do task.
  • The method delete() xoáa một to-do task.

Sau đó thêm các đường dẫn sau vào file routes/api.php:

<?php

Route::get('/todos/{todo}', 'App\Http\Controllers\TodoController@show');
Route::post('/todos', 'App\Http\Controllers\TodoController@create');
Route::put('/todos/{todo}', 'App\Http\Controllers\TodoController@update');
Route::delete('/todos/{todo}', 'App\Http\Controllers\TodoController@delete');

Configure Database Test

Chúng ta sẽ sử dụng cơ sở dữ liệu SQLite trong bộ nhớ để testing. Do đó, các testing của chúng ta sẽ được thực hiện nhanh hơn. Laravel đã hỗ trợ sử dụng cơ sở dữ liệu SQLite để thử nghiệm. Điều hướng đến tệp phpunit.xml nằm trong thư mục gốc của thư mục dự án của bạn, Nếu bạn dùng Mysql thì nên comment dòng sau lại:

<server name="DB_CONNECTION" value="sqlite"/>
<server name="DB_DATABASE" value=":memory:"/>

Viết Tests

Chúng ta đã thực hiện tất cả các cấu hình cần thiết, bây giờ chúng ta có thể bắt đầu viết các unit test.

Chạy lệnh Pest sau để tạo unit test:

$ php artisan pest:test TodoTest --unit

Lệnh này sẽ tạo TodoTest.php trong folder tests/Unit. Thay thế bằng đoạn code sau:

<?php

use App\Models\Todo;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(Tests\TestCase::class, RefreshDatabase::class);

it('does not create a to-do without a name field', function () {
    $response = $this->postJson('/api/todos', []);
    $response->assertStatus(422);
});

it('can create a to-do', function () {
    $attributes = Todo::factory()->raw();
    $response = $this->postJson('/api/todos', $attributes);
    $response->assertStatus(201)->assertJson(['message' => 'Todo has been created']);
    $this->assertDatabaseHas('todos', $attributes);
});

it('can fetch a to-do', function () {
    $todo = Todo::factory()->create();

    $response = $this->getJson("/api/todos/{$todo->id}");

    $data = [
        'message' => 'Retrieved To-do',
        'todo' => [
            'id' => $todo->id,
            'name' => $todo->name,
            'completed' => $todo->completed,
        ]
    ];

    $response->assertStatus(200)->assertJson($data);
});

it('can update a to-do', function () {
    $todo = Todo::factory()->create();
    $updatedTodo = ['name' => 'Updated To-do'];
    $response = $this->putJson("/api/todos/{$todo->id}", $updatedTodo);
    $response->assertStatus(200)->assertJson(['message' => 'To-do has been updated']);
    $this->assertDatabaseHas('todos', $updatedTodo);
});

it('can delete a to-do', function () {
    $todo = Todo::factory()->create();
    $response = $this->deleteJson("/api/todos/{$todo->id}");
    $response->assertStatus(200)->assertJson(['message' => 'To-do has been deleted']);
    $this->assertCount(0, Todo::all());
});

Ở đầu file, phương thức use() rằng buộc với class TestCase và trait RefreshDatabase. Base class TestCase được Laravel cung cấp và cung cấp các helper method để làm việc với framework trong quá trình testing. 

Trait RefreshDatabase hỗ trợ migrate và reset lai cơ sở dữ liệu sau mỗi lần test để dữ liệu từ lần test trước không ảnh hưởng đến các lần test tiếp theo.

Bây giờ, chúng ta hãy xem mỗi testing trong ví dụ trên sẽ thực hiện điều gì nhé:

  • it('does not create a to-do without a name field'): Laravel cung cấp một số helpers method để test API JSON và phản hồi của chúng. Ở đây, chúng ta sử dụng helper method postJson để tạo một truy vấn POST tới endpoint api/todos truyền vào một mảng trống. Sau đó, phương thức assertStatus() sẽ kiểm tra xem response được trả về với mã trạng thái có phải HTTP là 422 hay không. Trường hợp này đảm bảo rằng field name là bắt buộc.
  • it('can create a to-do'): Trường hợp này đảm bảo rằng một to-do task sẽ được tạo khi thực hiện request POST tới api/todos endpoint. Chúng ta khảng định rằng mã trạng thái HTTP là 201 được trả về và cơ sở dữ liệu chứa các to-do task bằng cách sử dụng assertDatabase().
  • it('can fetch a to-do'): Trường hợp này xác định một to-do task cụ thể có thể được lấy bằng ID. Tương tự, chúng ta khảng định rằng mã trạng thái được trả về là 200. Phương thức assertJson() chuyển đổi phản hồi thành một mảng và xác minh rằng mảng đã cho tồn tại trong phản hồi JSON do ứng dụng trả về hay không.
  • it('can update a to-do'): kiểm tra này đảm bảo rằng một to-do task có thể được cập nhật và task đã cập nhật có thể được tìm thấy trong cơ sở dữ liệu.
  • it('can delete a to-do'): kiểm tra này đảm bảo rằng to-do task có thể bị xóa và xác minh rằng tổng số task có trong cơ sở dữ liệu bằng không.

Chúng ta đã triển khai tương ứng các test case cho to-do task bây giờ là đến lúc chạy test thôi nào :

$ ./vendor/bin/pest --filter TodoTest

Và kết quả là :

Kết luận

Trong hướng dẫn này, chúng ta đã biết cách viết các test unit cho ứng dụng Laravel bằng cách sử dụng framework Pest test. Bạn có thể dap dụng nó vào trọng dự án của các bạn or xem nó như một bài để hiểu hơn về Pest test trong Laravel . Cảm ơn các bạn đã đọc bài viết này

Các bạn có thể vào doc của pest để đọc thêm nhé : Link

Bình luận (0)

Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough

Bài viết liên quan

Learning English Everyday