Files
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.2 KiB

02. Testing Frameworks: PHPUnit 10/11 vs Pest PHP v2/v3

So sánh chi tiết, cú pháp đối chiếu và quy tắc viết code giữa hai framework kiểm thử phổ biến nhất trong hệ sinh thái Laravel.


1. Bảng Đối Chiếu Cú Pháp (Cheat Sheet)

Tính năng PHPUnit 10/11 (Class-based) Pest PHP v2/v3 (Functional)
Khai báo Test #[Test] trên public method it('does something', function () { ... });
Khởi tạo (Setup) protected function setUp(): void beforeEach(function () { ... });
Dọn dẹp (Teardown) protected function tearDown(): void afterEach(function () { ... });
Assert giá trị bằng $this->assertSame($expected, $actual); expect($actual)->toBe($expected);
Assert kiểu dữ liệu $this->assertInstanceOf(User::class, $res); expect($res)->toBeInstanceOf(User::class);
Assert True/False $this->assertTrue($flag); expect($flag)->toBeTrue();
Assert Array có key $this->assertArrayHasKey('id', $data); expect($data)->toHaveKey('id');
Assert Count $this->assertCount(3, $items); expect($items)->toHaveCount(3);
Kỳ vọng Exception $this->expectException(CustomException::class); expect(fn() => ...)->toThrow(CustomException::class);
Data Provider #[DataProvider('provideData')] ->with([...]) hoặc ->with('dataset_name')

2. Chuẩn viết cho PHPUnit 10 & 11

Trong Laravel 10 và 11 với PHPUnit 10+, KHÔNG dùng DocBlock @test lỗi thời mà chuyển hẳn sang dùng PHP 8 Attributes:

<?php

namespace Tests\Unit\Services;

use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\DataProvider;

class OrderCalculationServiceTest extends TestCase
{
    private OrderCalculationService $service;

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

    #[Test]
    public function it_applies_tax_correctly(): void
    {
        // Arrange
        $amount = 100.0;
        $taxRate = 0.1; // 10%

        // Act
        $total = $this->service->calculateWithTax($amount, $taxRate);

        // Assert
        $this->assertSame(110.0, $total);
    }

    #[Test]
    #[DataProvider('invalidAmountProvider')]
    public function it_throws_exception_when_amount_is_negative(float $invalidAmount): void
    {
        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage('Amount must be positive');

        $this->service->calculateWithTax($invalidAmount, 0.1);
    }

    public static function invalidAmountProvider(): array
    {
        return [
            'negative amount' => [-10.0],
            'zero amount'     => [0.0],
        ];
    }
}

3. Chuẩn viết cho Pest PHP v2 & v3

Pest đem lại cú pháp ngắn gọn, dễ đọc như văn xuôi (BDD style):

<?php

use App\Services\OrderCalculationService;

beforeEach(function () {
    $this->service = new OrderCalculationService();
});

describe('OrderCalculationService', function () {
    it('applies tax correctly', function () {
        // Arrange
        $amount = 100.0;
        $taxRate = 0.1;

        // Act
        $total = $this->service->calculateWithTax($amount, $taxRate);

        // Assert
        expect($total)->toBe(110.0);
    });

    it('throws exception when amount is negative or zero', function (float $invalidAmount) {
        expect(fn () => $this->service->calculateWithTax($invalidAmount, 0.1))
            ->toThrow(\InvalidArgumentException::class, 'Amount must be positive');
    })->with([
        'negative amount' => -10.0,
        'zero amount'     => 0.0,
    ]);
});

4. Nguyên tắc Nhận diện Tự động của Agent

Khi Agent sinh code test cho một dự án:

  1. Đọc composer.json:
    • Nếu có package pestphp/pest -> BẮT BUỘC sinh file test theo chuẩn Pest.
    • Nếu chỉ có phpunit/phpunit:
      • Kiểm tra phiên bản PHP: Nếu PHP >= 8.1 và PHPUnit >= 10 -> Dùng PHP 8 Attributes (#[Test]).
      • Nếu PHP <= 8.0 hoặc PHPUnit 9 (Laravel 8/9) -> Dùng DocBlock /** @test */.