# Component Guide: Artisan Console Commands Artisan Commands xử lý các công việc chạy từ dòng lệnh (CLI), cron jobs, và lệnh bảo trì hệ thống. --- ## 1. Bản Chất Kiểm Thử Console Command Khi test một Artisan Command, cần kiểm tra: 1. **Exit Code**: Trả về `Command::SUCCESS` (0) khi hoàn thành, hoặc `Command::FAILURE` (1) / `Command::INVALID` (2) khi có lỗi. 2. **Output trên màn hình**: Kiểm tra dòng thông báo in ra bằng `expectsOutput()`, `doesntExpectOutput()`, `expectsTable()`. 3. **User Interaction**: Kiểm tra câu hỏi tương tác như `expectsConfirmation()`, `expectsQuestion()`, `expectsChoice()`. 4. **Service Execution**: Đảm bảo Command gọi đúng Service xử lý logic ngầm bên dưới. --- ## 2. Mã Mẫu Chuẩn: Console Command Test ### Command Nguồn: `App\Console\Commands\PruneOldLogsCommand` ```php namespace App\Console\Commands; use Illuminate\Console\Command; use App\Services\LogCleanupService; class PruneOldLogsCommand extends Command { protected $signature = 'logs:prune {--days=30 : Số ngày lưu log cũ} {--force : Thực thi mà không hỏi lại}'; protected $description = 'Xóa các file log cũ hơn số ngày quy định'; public function handle(LogCleanupService $cleanupService): int { $days = (int) $this->option('days'); if ($days <= 0) { $this->error('Số ngày phải lớn hơn 0.'); return self::INVALID; } if (!$this->option('force') && !$this->confirm("Bạn có chắc chắn muốn xóa log cũ hơn {$days} ngày?")) { $this->info('Đã hủy thao tác.'); return self::SUCCESS; } $deletedCount = $cleanupService->deleteLogsOlderThan($days); $this->info("Đã dọn dẹp thành công {$deletedCount} file log."); return self::SUCCESS; } } ``` ### Test Chuẩn: PHPUnit 10+ ```php namespace Tests\Unit\Console\Commands; use Tests\TestCase; use PHPUnit\Framework\Attributes\Test; use Mockery; use App\Services\LogCleanupService; use Illuminate\Console\Command; class PruneOldLogsCommandTest extends TestCase { #[Test] public function it_fails_when_days_option_is_zero_or_negative(): void { $this->artisan('logs:prune --days=0') ->expectsOutput('Số ngày phải lớn hơn 0.') ->assertExitCode(Command::INVALID); } #[Test] public function it_cancels_pruning_when_user_declines_confirmation(): void { $this->artisan('logs:prune --days=15') ->expectsConfirmation('Bạn có chắc chắn muốn xóa log cũ hơn 15 ngày?', 'no') ->expectsOutput('Đã hủy thao tác.') ->assertExitCode(Command::SUCCESS); } #[Test] public function it_prunes_logs_successfully_when_forced(): void { // Mock service $cleanupServiceMock = Mockery::mock(LogCleanupService::class); $cleanupServiceMock->shouldReceive('deleteLogsOlderThan') ->once() ->with(30) ->andReturn(12); $this->app->instance(LogCleanupService::class, $cleanupServiceMock); $this->artisan('logs:prune --days=30 --force') ->expectsOutput('Đã dọn dẹp thành công 12 file log.') ->assertExitCode(Command::SUCCESS); } } ```