Lorsque vous travaillez avec des applications Laravel, il est courant de rencontrer des scénarios dans lesquels une commande doit effectuer une tâche coûteuse. Pour éviter de bloquer le processus principal, vous pouvez décider de décharger la tâche vers une tâche pouvant être traitée par une file d'attente.
Prenons un exemple. Imaginez la commande app:import-users doit lire un gros fichier CSV et créer un utilisateur pour chaque entrée. Voici à quoi pourrait ressembler la commande :
/* ImportUsersCommand.php */ namespace App\Console\Commands; /*...*/ class ImportUsersCommand extends Command { protected $signature = 'app:import-users'; public function handle() { dispatch(new ImportUsersJob()); $this->line('Users imported successfully.'); $this->line('There are: ' . User::count(). ' Users.'); } }
Dans cet exemple, la commande distribue une tâche pour gérer la lecture du fichier et la création des utilisateurs. Voici à quoi pourrait ressembler le fichier ImportUsersJob.php :
/* ImportUsersJob.php */ namespace App\Jobs; /*...*/ class ImportUsersJob implements ShouldQueue { public function handle(FileReader $reader): void { foreach($reader->read('users.csv') as $data) { User::create([ 'name' => $data['name'], 'email' => $data['email'], ]); } } }
Lors du test de cette fonctionnalité, un test typique de la commande pourrait ressembler à ceci :
/* ImportUsersCommandTest.php */ namespace Tests\Feature; /*...*/ class ImportUsersCommandTest extends TestCase { use RefreshDatabase; public function test_it_processes_the_file(): void { Storage::fake('local')->put('users.csv', "..."); $this->artisan('app:import-users') ->expectsOutput('Users imported successfully.') ->expectsOutput('There are: 10 Users.') ->assertSuccessful(); $this->assertDatabaseCount('users', 10); } }
À première vue, ce test semble fonctionner parfaitement. L'exécution de la suite de tests affiche un résultat positif :
Cependant, lorsque vous exécutez la commande app:import-users dans un environnement réel, vous pouvez obtenir un résultat inattendu :
Comme vous pouvez le voir, le résultat de la commande indique qu'il y a 0 utilisateur dans la base de données. Alors, pourquoi cela arrive-t-il ?
La raison est que le travail est distribué dans une file d'attente, il ne s'exécute donc pas de manière synchrone avec l'exécution de la commande. Les utilisateurs seront créés uniquement lorsque la file d'attente traitera le travail ultérieurement.
La suite de tests utilise le pilote de file d'attente de synchronisation par défaut, ce qui signifie que les tâches sont traitées de manière synchrone pendant le test. En conséquence, le travail s'exécute immédiatement, donnant l'idée que tout fonctionne comme prévu.
Bien que ce comportement soit acceptable dans l'environnement de test, il est important de reconnaître que les résultats réels dépendent de la configuration QUEUE_CONNECTION dans votre environnement de production. Et compte tenu des exigences de votre projet, vous savez peut-être que le travail sera traité dans une file d'attente asynchrone.
Une fois que vous aurez pris conscience de cette distinction, vous souhaiterez peut-être améliorer vos tests pour éviter les « faux positifs ».
Tout d'abord, il est important de vérifier que la commande distribue réellement la tâche, que la tâche soit traitée de manière synchrone ou asynchrone. Voici comment tester cela :
/* ImportUsersCommandTest.php */ namespace Tests\Feature; /*...*/ class ImportUsersCommandTest extends TestCase { public function test_it_dispatches_the_job(): void { Queue:fake(); $this->artisan('app:import-users') ->expectsOutput('Process has been queued.') ->assertSuccessful(); Queue::assertPushed(ImportUsersJob::class); } }
Une fois que vous avez confirmé que la tâche est distribuée, vous pouvez tester le travail réel effectué par la tâche dans un test distinct. Voici comment vous pouvez structurer le test pour le poste :
/* ImportUsersJobTest.php */ namespace Tests\Feature; /*...*/ class ImportUsersJobTest extends TestCase { use refreshDatabase; public function test_it_processes_the_file() { Storage::fake('local')->put('users.csv', "..."); app()->call([new ImportUsersJob(), 'handle']); $this->assertDatabaseCount('users', 10); } }
Cela garantit que la tâche effectue le travail nécessaire, qu'elle soit traitée par une file d'attente ou de manière synchrone.
Comme dans la vraie vie, des cas extrêmes peuvent survenir et vous devez vous y préparer.
Le système de file d'attente de Laravel, selon la configuration de vos travailleurs, réessayera les tâches lorsqu'une exception se produit, et si les tentatives sont dépassées, la tâche sera marquée comme ayant échoué.
Alors, que se passe-t-il si le fichier n'existe pas ? Vous devez gérer de tels cas extrêmes en validant les entrées et en lançant des exceptions si nécessaire.
Voici comment vous pourriez gérer cela dans votre travail :
/* ImportUsersJobTest.php */ namespace App\Jobs; /*...*/ class ImportUsersJob implements ShouldQueue { use Queueable; public function handle(FileReader $reader): void { if(!Storage::disk('local')->exists('users.csv')){ throw new Exception('The users.csv file doesn\'t exist.') } foreach($reader->read('users.csv') as $data) { User::create([ 'name' => $data['name'], 'email' => $data['email'], ]); } } }
Voici comment tester ce scénario :
/* ImportUsersJobTest.php */ namespace Tests\Feature; /*...*/ class ImportUsersJobTest extends TestCase { use refreshDatabase; /*...*/ public function test_it_fails_when_file_doesnt_exist(): void { Storage::fake('local'); $this->expectException(Exception::class); $this->expectExceptionMessage('The users.csv file doesn\'t exist.'); dispatch(new ImportUsersJob()); } }
Cette approche garantit que vos tests reflètent plus précisément la manière dont les tâches seront traitées dans le monde réel.
La même stratégie peut être appliquée lorsqu'un contrôleur distribue une tâche dans une file d'attente ou lorsqu'un écouteur d'événement est mis en file d'attente.
Comme toujours, ajustez ces pratiques en fonction de votre projet et de votre équipe.
J'aimerais connaître votre avis !
Clause de non-responsabilité: Toutes les ressources fournies proviennent en partie d'Internet. En cas de violation de vos droits d'auteur ou d'autres droits et intérêts, veuillez expliquer les raisons détaillées et fournir une preuve du droit d'auteur ou des droits et intérêts, puis l'envoyer à l'adresse e-mail : [email protected]. Nous nous en occuperons pour vous dans les plus brefs délais.
Copyright© 2022 湘ICP备2022001581号-3