«Если рабочий хочет хорошо выполнять свою работу, он должен сначала заточить свои инструменты» — Конфуций, «Аналитики Конфуция. Лу Лингун»
титульная страница > программирование > Советы по тестированию заданий в очереди в Laravel

Советы по тестированию заданий в очереди в Laravel

Опубликовано 5 ноября 2024 г.
Просматривать:172

При работе с приложениями Laravel часто встречаются сценарии, когда команде необходимо выполнить дорогостоящую задачу. Чтобы избежать блокировки основного процесса, вы можете решить перенести задачу на задание, которое может быть обработано в очереди.

Давайте рассмотрим пример. Представьте себе, что команде app:import-users нужно прочитать большой файл CSV и создать пользователя для каждой записи. Вот как может выглядеть команда:

/* 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.');
    }
}

В этом примере команда отправляет задание на чтение файла и создание пользователей. Вот как может выглядеть 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'],
            ]);
        }
    }
}

При тестировании этой функции типичный тест команды может выглядеть следующим образом:

/* 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);
    }
}

На первый взгляд кажется, что этот тест работает идеально. Запуск набора тестов показывает успешный результат:

Tips for testing queued jobs in Laravel

Реальное исполнение

Однако при запуске команды app:import-users в реальной среде вы можете получить неожиданный результат:

Tips for testing queued jobs in Laravel

Как видите, выходные данные команды показывают, что в базе данных 0 пользователей. Итак, почему это происходит?

Причина в том, что задание отправляется в очередь, поэтому оно не выполняется синхронно с выполнением команды. Пользователи будут созданы только тогда, когда очередь позже обработает задание.

Почему тест проходит?

Набор тестов по умолчанию использует драйвер очереди синхронизации, то есть задания обрабатываются синхронно во время теста. В результате задание запускается немедленно, создавая впечатление, что все работает так, как ожидалось.

Хотя такое поведение приемлемо в тестовой среде, важно понимать, что реальные результаты зависят от конфигурации QUEUE_CONNECTION в вашей производственной среде. Учитывая требования вашего проекта, вы можете знать, что задание будет обработано в асинхронной очереди.

Как только вы осознаете это различие, возможно, вы захотите улучшить свои тесты, чтобы избежать «ложных срабатываний».

Тестирование вашей работы отправлено

Во-первых, важно убедиться, что команда действительно отправляет задание, независимо от того, обрабатывается ли задание синхронно или асинхронно. Вот как это проверить:

/* 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);
    }
}

Тестирование вашей работы выполнено

После того как вы подтвердите отправку задания, вы сможете проверить фактическую работу, выполняемую заданием, в отдельном тесте. Вот как вы можете структурировать тест для вакансии:

/* 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);
    }
}

Это гарантирует, что задание выполнит необходимую работу независимо от того, обрабатывается ли оно в очереди или синхронно.

Обработка пограничных случаев

Как и в реальной жизни, могут возникнуть крайние случаи, и вы должны быть к этому готовы.

Система очередей Laravel, в соответствии с конфигурацией ваших рабочих процессов, будет повторять задания при возникновении исключения, и если число повторных попыток будет превышено, задание будет помечено как неудачное.

Итак, что произойдет, если файл не существует? Такие крайние случаи необходимо обрабатывать, проверяя входные данные и при необходимости выдавая исключения.

Вот как вы можете справиться с этим в своей работе:

/* 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'],
            ]);
        }
    }
}

Вот как можно протестировать этот сценарий:

/* 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());
    }
}

Заключительные мысли

Такой подход гарантирует, что ваши тесты более точно отражают, как задания будут обрабатываться в реальном мире.
Ту же стратегию можно применить, когда контроллер отправляет задание в очередь или когда в очереди стоит прослушиватель событий.
Как всегда, корректируйте эти методы в соответствии с вашим проектом и командой.

Мне бы хотелось услышать ваше мнение!

Заявление о выпуске Эта статья воспроизведена по адресу: https://dev.to/eduarguz/tips-for-testing-queued-jobs-in-laravel-4c77?1. Если есть какие-либо нарушения, свяжитесь с [email protected], чтобы удалить их.
Последний учебник Более>

Изучайте китайский

Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.

Copyright© 2022 湘ICP备2022001581号-3