2045c856e9
- 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>
129 lines
4.2 KiB
Markdown
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 */`**.
|