feat: enforce exhaustive unit testing and 100% SonarQube branch coverage

- Enforce mandatory handle() and failed() method testing for all Jobs and Commands
- Ban shallow reflection-only helper testing in Jobs
- Mandate actual SQLite in-memory query execution for Repositories (ban Builder::class assertions alone)
- Enforce full branch coverage for SonarQube (ternary, null-coalescing, catch blocks, match)
- Update JobTest templates for PHPUnit and Pest to include handle() and failed() methods
- Update verification checklist and master system prompt with strict branch gatekeepers

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-08 15:01:01 +07:00
parent c29bfb9c31
commit 0dce2ef812
7 changed files with 96 additions and 16 deletions
@@ -98,3 +98,20 @@ class UserRepositoryTest extends TestCase
}
}
```
---
## 3. Bẫy Nghiêm Trọng Cần Tránh Khi Test Repository
### ❌ Bẫy Anti-Pattern: Chỉ kiểm tra `assertInstanceOf(Builder::class)`
Nhiều bài test chỉ viết:
```php
$query = $repo->search(['name' => 'ABC']);
$this->assertInstanceOf(Builder::class, $query); // ❌ SAI LẦM: Chỉ chạm đúng 1-2 dòng return!
```
- **Hậu quả**: Toàn bộ logic bên trong các câu `where()`, `join()`, `groupBy()`, `orderBy()`, xử lý lọc ngày tháng, lọc trạng thái... **hoàn toàn không được thực thi**, dẫn đến độ phủ SonarQube của Repository chỉ đạt 10% - 25%!
- **Quy tắc vàng**:
1. Kết hợp trait `use RefreshDatabase;` với SQLite in-memory siêu tốc.
2. Tạo bản ghi mẫu bằng `Model::factory()->create(...)` thỏa mãn và không thỏa mãn điều kiện lọc.
3. Thực thi query lấy kết quả thật (`$results = $query->get()` hoặc gọi thẳng method `$repo->findActive(...)`) và assert chính xác số lượng bản ghi (`assertCount`) và ID của các bản ghi thỏa mãn.
4. Mọi phương thức update, delete, bulkInsert của Repository đều phải được gọi và assert trạng thái database bằng `$this->assertDatabaseHas(...)` hoặc `$this->assertDatabaseMissing(...)`.
+13
View File
@@ -109,3 +109,16 @@ class SendWelcomeEmailJobTest extends TestCase
}
}
```
---
## 3. Bẫy Nghiêm Trọng Cần Tránh Khi Test Jobs
### ❌ Bẫy Anti-Pattern: Chỉ dùng Reflection test hàm helper nhỏ
Nhiều AI và lập trình viên hay dùng `ReflectionMethod` để test các hàm helper nhỏ như `buildRedisKey()`, `getFilePath()` và bỏ qua hoàn toàn phương thức chính **`handle()`**.
- **Hậu quả**: SonarQube báo độ phủ của Job chỉ đạt 1% - 10%, vì 90% dòng code nằm trong `handle()` không hề được chạy!
- **Quy tắc vàng**:
1. **BẮT BUỘC 100% phải test method `handle()`**.
2. Mock toàn bộ dependencies của `handle()`: Storage (`Storage::fake()`), Database/Repository, Redis (`Redis::shouldReceive()`), Mailer.
3. Phải có bài test cho các trường hợp rẽ nhánh trong `handle()`: khi dữ liệu rỗng, khi export thành công, khi import lỗi từng dòng.
4. Phải có bài test gọi method `failed(?Throwable $e)` để kiểm tra việc ghi log hoặc bắn alert khi job thất bại.