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

129 lines
4.2 KiB
Markdown

# 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
<?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
<?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 */`**.