Files
agent-unit-testing/docs/components/03-form-requests-and-rules.md
T
Pham-Tan-Thien 2045c856e9 feat: complete Smartgift AI Laravel unit testing toolkit
- Add 10 core testing guides (setup, AAA principles, PHPUnit/Pest, mocking, anti-patterns, PHP 8.4+, debugging, data factories, advanced edge cases)
- Add 13 component testing guides covering all Laravel layers
- Add standard templates for PHPUnit 10/11 and Pest v2/v3 including ArchitectureTest
- Add Docker smart test runner, dependency installer, and CI/CD templates (GitHub Actions, GitLab CI)
- Add 4 Claude Code skills (laravel-test-setup, laravel-unit-test with --all mode, laravel-test-review, laravel-mock-helper)
- Add single unified installer script (sg-install-unit-testing.sh) with global support, CLI runners, and 1-click updater (sg-test-update)
- Add comprehensive documentation (README.md, USAGE-GUIDE.md)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-07 16:41:37 +07:00

4.4 KiB

Component Guide: Form Requests & Custom Validation Rules

Kiểm thử FormRequest và Custom Validation Rules giúp đảm bảo tính toàn vẹn dữ liệu đầu vào và các ràng buộc phân quyền mà không cần tạo request HTTP thật.


1. Bản Chất Kiểm Thử

  • FormRequest:
    • Test phương thức authorize(): Trả về true khi user có quyền, false khi không có quyền.
    • Test phương thức rules(): Xác nhận các field bắt buộc, format email, min/max length, regex,...
  • Custom Rule:
    • Test logic hàm validate() (Laravel 10/11 ValidationRule) hoặc passes() (Laravel 9 Rule).
    • Đảm bảo thông báo lỗi khi validation fail là chính xác.

2. Mã Mẫu Chuẩn: Custom Validation Rule

Rule Nguồn: App\Rules\PhoneNumberRule (Laravel 10/11)

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class PhoneNumberRule implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        // Số điện thoại Việt Nam bắt đầu bằng 0 và có 10 chữ số
        if (!is_string($value) || !preg_match('/^0[0-9]{9}$/', $value)) {
            $fail('Trường :attribute phải là số điện thoại Việt Nam hợp lệ gồm 10 chữ số.');
        }
    }
}

Test Chuẩn: PHPUnit 10+

namespace Tests\Unit\Rules;

use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\DataProvider;
use App\Rules\PhoneNumberRule;

class PhoneNumberRuleTest extends TestCase
{
    private PhoneNumberRule $rule;

    protected function setUp(): void
    {
        parent::setUp();
        $this->rule = new PhoneNumberRule();
    }

    #[Test]
    #[DataProvider('validPhoneNumberProvider')]
    public function it_passes_with_valid_phone_number(string $validPhone): void
    {
        $failed = false;
        $failCallback = function () use (&$failed) {
            $failed = true;
        };

        $this->rule->validate('phone', $validPhone, $failCallback);

        $this->assertFalse($failed, "Phone number '{$validPhone}' should be valid.");
    }

    #[Test]
    #[DataProvider('invalidPhoneNumberProvider')]
    public function it_fails_with_invalid_phone_number(mixed $invalidPhone): void
    {
        $errorMessage = '';
        $failCallback = function (string $message) use (&$errorMessage) {
            $errorMessage = $message;
        };

        $this->rule->validate('phone', $invalidPhone, $failCallback);

        $this->assertNotEmpty($errorMessage);
        $this->assertStringContainsString('số điện thoại Việt Nam hợp lệ', $errorMessage);
    }

    public static function validPhoneNumberProvider(): array
    {
        return [
            'valid mobile 1' => ['0912345678'],
            'valid mobile 2' => ['0868888888'],
            'valid mobile 3' => ['0345678901'],
        ];
    }

    public static function invalidPhoneNumberProvider(): array
    {
        return [
            'starts with 1'       => ['1912345678'],
            'too short (9 digits)'=> ['091234567'],
            'too long (11 digits)'=> ['09123456789'],
            'contains letters'    => ['09123abcde'],
            'empty string'        => [''],
            'non-string (array)'  => [['0912345678']],
        ];
    }
}

3. Mã Mẫu Chuẩn: FormRequest Validation Rules & Authorize

namespace Tests\Unit\Http\Requests;

use Tests\TestCase;
use PHPUnit\Framework\Attributes\Test;
use App\Http\Requests\CreatePostRequest;
use Illuminate\Support\Facades\Validator;

class CreatePostRequestTest extends TestCase
{
    #[Test]
    public function it_validates_required_fields(): void
    {
        $request = new CreatePostRequest();
        $rules = $request->rules();

        // 1. Test payload thiếu title
        $validator = Validator::make(['content' => 'Sample content'], $rules);
        $this->assertTrue($validator->fails());
        $this->assertArrayHasKey('title', $validator->errors()->toArray());

        // 2. Test payload đầy đủ
        $validData = [
            'title'   => 'Tiêu đề bài viết hợp lệ',
            'content' => 'Nội dung bài viết đầy đủ chi tiết',
        ];
        $validator = Validator::make($validData, $rules);
        $this->assertFalse($validator->fails());
    }
}