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>
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
# Component Guide: Observers & Custom Exceptions
|
||||
|
||||
Kiểm thử Model Observers và Custom Exception Handlers đảm bảo các hành động phản ứng tự động (Side-effects) và xử lý lỗi được thực hiện đồng nhất.
|
||||
|
||||
---
|
||||
|
||||
## 1. Kiểm Thử Model Observers
|
||||
|
||||
- **Bản chất**: Observer lắng nghe các lifecycle events của Model (`creating`, `created`, `updating`, `deleted`,...).
|
||||
- **Cách test đơn vị**: Khởi tạo trực tiếp instance của Observer và gọi phương thức (ví dụ: `$observer->creating($model)`) mà không cần lưu vào database.
|
||||
|
||||
### Observer Nguồn: `App\Observers\UserObserver`
|
||||
```php
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class UserObserver
|
||||
{
|
||||
public function creating(User $user): void
|
||||
{
|
||||
// Tự động gán UUID nếu chưa có
|
||||
if (empty($user->uuid)) {
|
||||
$user->uuid = (string) Str::uuid();
|
||||
}
|
||||
|
||||
// Tự động chuẩn hóa email về chữ thường
|
||||
$user->email = strtolower($user->email);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Test Chuẩn: PHPUnit 10+
|
||||
```php
|
||||
namespace Tests\Unit\Observers;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use App\Observers\UserObserver;
|
||||
use App\Models\User;
|
||||
|
||||
class UserObserverTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
public function it_generates_uuid_and_lowercases_email_on_creating(): void
|
||||
{
|
||||
// Arrange
|
||||
$user = new User(['email' => 'JOHN.DOE@EXAMPLE.COM']);
|
||||
$observer = new UserObserver();
|
||||
|
||||
// Act
|
||||
$observer->creating($user);
|
||||
|
||||
// Assert
|
||||
$this->assertNotEmpty($user->uuid);
|
||||
$this->assertSame('john.doe@example.com', $user->email);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Kiểm Thử Custom Exceptions
|
||||
|
||||
Kiểm thử Custom Exception đảm bảo:
|
||||
- Exception mang đúng mã HTTP status code.
|
||||
- Exception render đúng cấu trúc JSON khi được kích hoạt trong API context.
|
||||
|
||||
### Exception Nguồn: `App\Exceptions\InsufficientBalanceException`
|
||||
```php
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class InsufficientBalanceException extends Exception
|
||||
{
|
||||
public function __construct(
|
||||
public readonly int $currentBalance,
|
||||
public readonly int $requiredAmount
|
||||
) {
|
||||
parent::__construct('Số dư tài khoản không đủ để thực hiện giao dịch.');
|
||||
}
|
||||
|
||||
public function render(Request $request): JsonResponse
|
||||
{
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'code' => 'INSUFFICIENT_BALANCE',
|
||||
'message' => $this->getMessage(),
|
||||
'current_balance' => $this->currentBalance,
|
||||
'required_amount' => $this->requiredAmount,
|
||||
], Response::HTTP_PAYMENT_REQUIRED);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Test Chuẩn: PHPUnit 10+
|
||||
```php
|
||||
namespace Tests\Unit\Exceptions;
|
||||
|
||||
use Tests\TestCase;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use App\Exceptions\InsufficientBalanceException;
|
||||
|
||||
class InsufficientBalanceExceptionTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
public function it_renders_custom_json_response_with_balance_details(): void
|
||||
{
|
||||
$exception = new InsufficientBalanceException(currentBalance: 50_000, requiredAmount: 120_000);
|
||||
$request = Request::create('/api/payment', 'POST');
|
||||
|
||||
$response = $exception->render($request);
|
||||
|
||||
$this->assertSame(Response::HTTP_PAYMENT_REQUIRED, $response->getStatusCode());
|
||||
|
||||
$data = json_decode($response->getContent(), true);
|
||||
$this->assertSame('INSUFFICIENT_BALANCE', $data['code']);
|
||||
$this->assertSame(50_000, $data['current_balance']);
|
||||
$this->assertSame(120_000, $data['required_amount']);
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user