Lập trình · 18/09/2026

Kiểm thử tự động Laravel với Pest: Từ test đầu tiên đến quy trình CI

Kiểm thử tự động không nhằm chứng minh phần mềm không có lỗi; nó tạo ra tín hiệu sớm khi hành vi quan trọng bị thay đổi ngoài ý muốn. Với Laravel và Pest, nhóm có thể bắt đầu từ các luồng nghiệp vụ nhỏ, viết test dễ đọc và dần xây dựng mạng lưới bảo vệ cho những phần rủi ro nhất của hệ thống.

Kiểm thử tự động Laravel với Pest: Từ test đầu tiên đến quy trình CI

Kiểm thử tự động không nhằm chứng minh phần mềm không có lỗi; nó tạo ra tín hiệu sớm khi hành vi quan trọng bị thay đổi ngoài ý muốn. Với Laravel và Pest, nhóm có thể bắt đầu từ các luồng nghiệp vụ nhỏ, viết test dễ đọc và dần xây dựng mạng lưới bảo vệ cho những phần rủi ro nhất của hệ thống.

Nên bắt đầu từ đâu?

Đừng đặt mục tiêu phủ toàn bộ code ngay lập tức. Hãy ưu tiên những hành vi nếu hỏng sẽ gây thiệt hại rõ ràng: đăng nhập, phân quyền, tạo đơn hàng, thanh toán, cập nhật tồn kho, API tích hợp và các lỗi từng xuất hiện trên production.

Loại testPhù hợp vớiĐặc điểm
UnitHàm tính toán, quy tắc nghiệp vụ độc lậpChạy nhanh, ít phụ thuộc framework
FeatureHTTP, database, middleware, quyền truy cậpCho tín hiệu gần với hành vi thực tế
BrowserLuồng giao diện quan trọngGiá trị cao nhưng chậm và cần chăm sóc nhiều hơn
ArchitectureQuy tắc cấu trúc và dependencyNgăn codebase lệch khỏi convention

Test đầu tiên bằng Pest

<?php

it('redirects guests away from the dashboard', function () {
    $response = $this->get('/dashboard');

    $response->assertRedirect('/login');
});

Tên test nên diễn đạt kết quả quan sát được. Tránh đặt tên theo method nội bộ, vì refactor có thể đổi method nhưng hành vi người dùng vẫn giữ nguyên.

Kiểm tra một luồng có database

<?php

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

uses(RefreshDatabase::class);

it('allows an active user to update their profile', function () {
    $user = User::factory()->create(['is_active' => true]);

    $response = $this->actingAs($user)->put('/profile', [
        'name' => 'Nguyen Minh An',
        'email' => 'an@example.test',
    ]);

    $response->assertRedirect('/profile');
    $this->assertDatabaseHas('users', [
        'id' => $user->id,
        'name' => 'Nguyen Minh An',
    ]);
});

RefreshDatabase giúp mỗi test bắt đầu từ trạng thái dữ liệu kiểm soát được. Factory tạo bản ghi rõ ý nghĩa hơn việc phụ thuộc vào database có sẵn trên máy người viết test.

Test cả đường thành công và đường từ chối

Chỉ kiểm tra trường hợp hợp lệ dễ bỏ sót lỗ hổng. Với endpoint cập nhật hồ sơ, nên thêm các tình huống như người dùng chưa đăng nhập, email sai định dạng, email trùng và tài khoản bị khóa. Với phân quyền, hãy kiểm tra cả người có quyền và người không có quyền.

Test giá trị cao bảo vệ một cam kết nghiệp vụ. Nó không cần biết controller gọi bao nhiêu method; nó cần chứng minh người dùng đúng vai trò nhận được đúng kết quả.

Giữ test ổn định và dễ bảo trì

  • Dùng factory state như active() hoặc admin() để dữ liệu thể hiện rõ ý định.
  • Không gọi API bên thứ ba thật; dùng fake cho mail, queue, notification, storage và HTTP client.
  • Đóng băng thời gian khi logic liên quan đến hạn dùng hoặc lịch chạy.
  • Không phụ thuộc thứ tự test; mỗi test phải tự chuẩn bị trạng thái.
  • Ưu tiên assertion về kết quả nghiệp vụ, hạn chế khóa test vào HTML hoặc cấu trúc nội bộ.

Fake các tác vụ phụ

use Illuminate\Support\Facades\Queue;
use App\Jobs\SendWelcomeEmail;

it('queues a welcome email after registration', function () {
    Queue::fake();

    $this->post('/register', [
        'name' => 'Lan',
        'email' => 'lan@example.test',
        'password' => 'Strong-password-123',
        'password_confirmation' => 'Strong-password-123',
    ])->assertRedirect('/dashboard');

    Queue::assertPushed(SendWelcomeEmail::class);
});

Chạy nhanh trên máy lập trình

php artisan test
php artisan test --stop-on-failure
php artisan test --filter=profile
php artisan test --parallel --processes=4

Chạy song song có thể rút ngắn thời gian, nhưng test phải cô lập tài nguyên. Tệp dùng chung, cổng mạng cố định hoặc dịch vụ bên ngoài có thể gây lỗi ngẫu nhiên giữa các process.

Đưa test vào CI

name: tests
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'
      - run: composer install --no-interaction --prefer-dist
      - run: cp .env.example .env
      - run: php artisan key:generate
      - run: php artisan test

Đây là khung khởi đầu. Dự án dùng MySQL, Redis hoặc trình duyệt cần thêm service và cấu hình tương ứng. Không dùng thông tin bí mật production cho CI và phải tách riêng database test.

Dấu hiệu test đang thành gánh nặng

  • Test thất bại ngẫu nhiên khi code không đổi.
  • Một thay đổi giao diện nhỏ làm hỏng nhiều test không liên quan.
  • Fixture quá lớn khiến người đọc không biết dữ liệu nào quan trọng.
  • Test mock mọi lớp trung gian và chỉ xác nhận cách triển khai hiện tại.
  • Bộ test chậm đến mức nhóm bỏ qua trước khi push.

Khi thấy các dấu hiệu này, hãy giảm dữ liệu, cô lập dịch vụ ngoài, chia test theo mức độ và giữ một nhóm smoke test chạy nhanh. Xóa test trùng lặp cũng là bảo trì có giá trị.

Lộ trình áp dụng thực tế

  1. Chọn ba đến năm luồng kinh doanh quan trọng nhất.
  2. Viết feature test cho thành công, validation và phân quyền.
  3. Thêm regression test mỗi khi sửa một lỗi production.
  4. Chạy nhóm test nhanh trên mọi pull request.
  5. Theo dõi thời gian chạy và lỗi không ổn định.
  6. Mở rộng sang browser test hoặc architecture test khi giá trị đã rõ.

Kết luận

Một bộ test hữu ích không được đo chỉ bằng phần trăm coverage. Nó được đo bằng khả năng phát hiện thay đổi nguy hiểm sớm, giải thích lỗi rõ và chạy đủ nhanh để đội ngũ sử dụng thường xuyên. Với Laravel và Pest, hãy bắt đầu bằng hành vi quan trọng, quản lý dữ liệu chặt chẽ và để CI biến các cam kết đó thành bước kiểm tra lặp lại được.

Nguồn tham khảo

Thảo luận

Bình luận 0

Đăng nhập để bình luận

Bạn cần có tài khoản để tham gia thảo luận và trả lời độc giả khác.

Đăng nhậpĐăng ký

Chưa có bình luận. Hãy là người đầu tiên chia sẻ ý kiến.