# 04. Naming Conventions & Test Structure Quy chuẩn đặt tên, ánh xạ thư mục và tổ chức mã nguồn kiểm thử trong Laravel để đảm bảo tính nhất quán và rõ ràng cho cả lập trình viên lẫn công cụ CI/CD. --- ## 1. Ánh xạ Cấu trúc Thư mục (Directory Mapping) Cấu trúc thư mục của file Test trong `tests/Unit/` phải **phản chiếu 1-1 (Mirror)** cấu trúc của class nguồn trong `app/`: ```text app/ ├── Actions/ │ └── User/ │ └── CreateUserAction.php ├── Services/ │ └── Payment/ │ └── StripePaymentService.php ├── Rules/ │ └── ValidTaxCodeRule.php └── Jobs/ └── ProcessPodcastJob.php tests/ └── Unit/ ├── Actions/ │ └── User/ │ └── CreateUserActionTest.php ├── Services/ │ └── Payment/ │ └── StripePaymentServiceTest.php ├── Rules/ │ └── ValidTaxCodeRuleTest.php └── Jobs/ └── ProcessPodcastJobTest.php ``` --- ## 2. Quy ước Đặt tên File & Class Test - **Quy tắc**: Tên file test = `Test.php`. - **Namespace**: Tương ứng theo namespace của class nguồn nhưng bắt đầu bằng `Tests\Unit\...`. - **Ví dụ**: - Class nguồn: `App\Services\InvoiceService` - Class test: `Tests\Unit\Services\InvoiceServiceTest` --- ## 3. Quy ước Đặt tên Phương thức Test (Test Method Naming) Mỗi tên method test phải đọc như một câu hoàn chỉnh, thể hiện rõ **Hành vi (Action)** và **Kết quả kỳ vọng (Expected Outcome)** trong một **Điều kiện cụ thể (Context)**. ### Cú pháp chuẩn PHPUnit: Dùng định dạng `snake_case` bắt đầu bằng `test_it_...` hoặc `it_...` (nếu có attribute `#[Test]`): 1. **Happy Path (Thành công)**: - `it_creates_order_and_dispatches_event_when_inventory_is_available()` - `it_returns_discounted_amount_for_vip_customer()` 2. **Failure / Edge Case (Thất bại / Biên)**: - `it_throws_out_of_stock_exception_when_inventory_is_zero()` - `it_fails_validation_when_email_domain_is_blacklisted()` 3. **Authorization (Phân quyền)**: - `it_denies_access_when_user_is_not_admin()` ### Cú pháp chuẩn Pest PHP: Dùng chuỗi mô tả tự nhiên trong hàm `it()` hoặc `test()`: ```php describe('InvoiceService', function () { it('creates invoice and dispatches event when payment succeeds', function () { ... }); it('throws insufficient funds exception when balance is too low', function () { ... }); it('denies access when user is not admin', function () { ... }); }); ``` --- ## 4. Tổ chức Data Providers Khi một method cần test với nhiều bộ dữ liệu (Input -> Output) khác nhau: ### Quy tắc PHPUnit: - Đặt tên provider: `_provider` hoặc `provide_`. - Khai báo là `public static function`. - Luôn đặt **Key dạng chuỗi có ý nghĩa** cho từng data set (để khi test fail, CLI báo rõ data set nào bị hỏng thay vì chỉ báo index số `0, 1, 2`): ```php #[Test] #[DataProvider('emailValidationProvider')] public function it_validates_email_format(string $email, bool $expectedResult): void { $validator = new EmailValidator(); $this->assertSame($expectedResult, $validator->isValid($email)); } public static function emailValidationProvider(): array { return [ 'standard email' => ['user@example.com', true], 'email with sub-domain' => ['user@corp.example.co.uk', true], 'missing at symbol' => ['userexample.com', false], 'missing domain' => ['user@', false], 'whitespace in email' => ['user @example.com', false], ]; } ``` ### Quy tắc Pest PHP: ```php it('validates email format', function (string $email, bool $expectedResult) { $validator = new EmailValidator(); expect($validator->isValid($email))->toBe($expectedResult); })->with([ 'standard email' => ['user@example.com', true], 'email with sub-domain' => ['user@corp.example.co.uk', true], 'missing at symbol' => ['userexample.com', false], ]); ```