Logo

dev-resources.site

for different kinds of informations.

Testando filas em projetos Laravel

Published at
12/14/2023
Categories
php
laravel
phpunit
qa
Author
themattos
Categories
4 categories in total
php
open
laravel
open
phpunit
open
qa
open
Author
9 person written this
themattos
open
Testando filas em projetos Laravel

Os testes unitários desempenham um papel crucial no desenvolvimento de software, garantindo que cada parte do código funcione conforme o esperado. Em projetos Laravel, onde a utilização de filas é comum para processamento em segundo plano, é essencial garantir que essas filas sejam testadas de forma adequada.

Vamos implementar testes na fila de sincronização de dados do projeto product-api.

Product API

Rode o composer install:

composer install
Enter fullscreen mode Exit fullscreen mode

Após a instalação, o script ./init.sh será chamado para:

  • Instalar o MySQL
  • Criar o banco usado pela API.
  • Criar no banco o usuário utilizado pela API.
  • Rodar as migrações.
  • Gerar o swaager.json
  • Rodar os testes.
  • Startar a aplicação.

Laravel Logo

Build Status Total Downloads Latest Stable Version License

About Laravel

Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:

Laravel is accessible, powerful, and provides tools required for large, robust applications.

Learning Laravel

Laravel has the most extensive and thorough documentation and video…




Antes de começar, confira meu artigo onde explico como implementar o Laravel Queue em seu projeto, seguindo padrões para manter seu código testável.

No meu caso, optei por criar os testes após a implementação da fila. Contudo, vale destacar que a abordagem inversa, onde os testes são criados antes da implementação, também é válida e pode ser adotada conforme a preferência em seu projeto.

Crie um novo teste com o comando artisan make:test. Para fins de organização, criarei meus testes dentro da pasta Plataform1, pois meu job faz sinc de produtos com esse módulo.

php artisan make:test Plataform1/ProductSyncTest
Enter fullscreen mode Exit fullscreen mode

O primeiro método que vamos criar, será responsável por testar o despache do nosso job para a fila. Não estranhe o tamanho do nome do método, pois prefiro que seja descritivo e claro em relação à sua funcionalidade.

Vamos usar umaQueue fake para testar os despaches e vamos despachar os jobs.

        Queue::fake();

        $productSync = new ProductSync();

        $queue = 'data_sync';

        ProductSyncJob::dispatch($productSync)->onQueue($queue);
Enter fullscreen mode Exit fullscreen mode

Agora, verificaremos se ProductSyncJob foi despachado para data_sync.

Queue::assertPushedOn($queue, ProductSyncJob::class);
Enter fullscreen mode Exit fullscreen mode

Vamos despachar mais dois jobs e fazer uma nova afirmação.

        Queue::bulk([
            new ProductSyncJob($productSync),
            new ProductSyncJob($productSync),
        ], '', $queue);

        Queue::assertPushed(ProductSyncJob::class, 3);
Enter fullscreen mode Exit fullscreen mode

Caso você implemente o despache de jobs com o facade Bus, também poderá testa-lo.

        Bus::fake();

        Bus::batch([
            new ProductSyncJob($productSync),
            new ProductSyncJob($productSync),
        ])->onQueue($queue)->dispatch();

        Bus::assertBatched(function (PendingBatchFake $batch) {
            return count($batch->jobs) === 2;
        });
Enter fullscreen mode Exit fullscreen mode

A implementação do nosso primeiro teste ficará assim.

<?php

namespace Tests\Feature\Plataform1;

use App\Jobs\Plataform1\ProductSyncJob;
use App\Services\Plataform1\ProductSync;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Testing\Fakes\PendingBatchFake;
use Tests\TestCase;

class ProductSyncTest extends TestCase
{
    public function test_dispatching_the_product_sync_job_to_the_queue(): void
    {
        Queue::fake();

        $productSync = new ProductSync();

        $queue = 'data_sync';

        ProductSyncJob::dispatch($productSync)->onQueue($queue);

        Queue::assertPushedOn($queue, ProductSyncJob::class);

        Queue::bulk([
            new ProductSyncJob($productSync),
            new ProductSyncJob($productSync),
        ], '', $queue);

        Queue::assertPushed(ProductSyncJob::class, 3);

        Bus::fake();

        Bus::batch([
            new ProductSyncJob($productSync),
            new ProductSyncJob($productSync),
        ])->onQueue($queue)->dispatch();

        Bus::assertBatched(function (PendingBatchFake $batch) {
            return count($batch->jobs) === 2;
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

Você pode ajustar o teste de acordo com a necessidade do seu projeto, Laravel é um framework altamente testável e rico em recursos de teste.

Agora, vamos criar um teste chamado test_product_sync para o serviço ProductSync que injetamos em ProductSyncJob. Conhecer a implementação de ProductSync é fundamental para o entendimento do próximo teste. Você pode verificar sua implementação no artigo ou repositório mencionado anteriormente.

Vamos pegar a url que recebe as requisições e criar dois arrays de produtos.

        $plataform1Url = config('integration.plataform1')['api']['url'];

        $data1 = Plataform1Product::factory()
            ->count(10)
            ->make()
            ->toArray();

        $data2 = Plataform1Product::factory()
            ->count(10)
            ->make()
            ->toArray();
Enter fullscreen mode Exit fullscreen mode

Agora, criaremos uma closure que retornará nosso response baseado nos parâmetros.

        $json = fn ($page, $nextPage) => [
            'next_page' => $nextPage,
            'data' => match ($page) {
                1 => $data1,
                2 => $data2,
            }
        ];
Enter fullscreen mode Exit fullscreen mode

Então, vamos mockar os requests recebidos no endpoint, instanciar ProductSync e chamar o método execute.

        Http::fake([
            $plataform1Url.'/v1/products?page=1' => Http::response($json(1, 2)),
            $plataform1Url.'/v1/products?page=2' => Http::response($json(2, null)),
        ]);

        $productSync = new ProductSync();

        $productSync->execute();
Enter fullscreen mode Exit fullscreen mode

Faremos três afirmações, que nossa tabela de produto tem um produto de $data1, um produto de $data2 e que após truncar, o produto estará ausente.

        $this->assertDatabaseHas('product', ['id' => $data1[0]['id']]);

        $this->assertDatabaseHas('product', ['id' => $data2[0]['id']]);

        Product::truncate();

        $this->assertDatabaseMissing('product', ['id' => $data1[0]['id']]);
Enter fullscreen mode Exit fullscreen mode

A implementação do nosso segundo teste ficará assim.

    public function test_product_sync(): void
    {
        $plataform1Url = config('integration.plataform1')['api']['url'];

        $data1 = Plataform1Product::factory()
            ->count(10)
            ->make()
            ->toArray();

        $data2 = Plataform1Product::factory()
            ->count(10)
            ->make()
            ->toArray();

        $json = fn ($page, $nextPage) => [
            'next_page' => $nextPage,
            'data' => match ($page) {
                1 => $data1,
                2 => $data2,
            }
        ];

        Http::fake([
            $plataform1Url.'/v1/products?page=1' => Http::response($json(1, 2)),
            $plataform1Url.'/v1/products?page=2' => Http::response($json(2, null)),
        ]);

        $productSync = new ProductSync();

        $productSync->execute();

        $this->assertDatabaseHas('product', ['id' => $data1[0]['id']]);

        $this->assertDatabaseHas('product', ['id' => $data2[0]['id']]);

        Product::truncate();

        $this->assertDatabaseMissing('product', ['id' => $data1[0]['id']]);
    }
Enter fullscreen mode Exit fullscreen mode

Antes de executarmos os testes, realizaremos algumas configurações para que o teste utilize um banco SQLite em memória.

Em phpunit.xml adicione as seguintes variáveis.

        <env name="DB_CONNECTION" value="sqlite"/>
        <env name="DB_DATABASE" value=":memory:"/>
Enter fullscreen mode Exit fullscreen mode

Dentro de nossa classe de teste, use a trait RefreshDatabase.

    use RefreshDatabase;
Enter fullscreen mode Exit fullscreen mode

Finalmente, vamos rodar nossos testes.

php artisan test

// opcionalmente, você pode passar o caminho do arquivo que deseja testar
php artisan test path/to/file
Enter fullscreen mode Exit fullscreen mode

php artisan test

Neste artigo, exploramos a importância dos testes em filas de projetos Laravel, destacando como eles fortalecem a confiabilidade das aplicações. Ao adotar práticas de teste eficazes, garantimos que a execução assíncrona e a gestão de filas sejam mais consistentes. A integração de testes sólidos contribui para a construção de sistemas mais resilientes em ambientes de produção.

phpunit Article's
30 articles in total
Favicon
6 Steps to Master PHPUnit Testing with Ease!
Favicon
Mastering Unit Testing in PHP: Tools, Frameworks, and Best Practices
Favicon
PHP: Should I mock or should I go?
Favicon
Focusing your tests on the domain. A PHPUnit example
Favicon
Understanding Mock Objects in PHPUnit Testing
Favicon
Wrote a book (And it is not about coding, and yeap it is in Greek)
Favicon
How to run a phpunit unit test with a specific dataset
Favicon
Test Your DOM in Laravel with PHPUnit
Favicon
PHP: Mocking Closures and performing assertions
Favicon
Run PHPUnit locally in your WordPress Plugin with DDEV
Favicon
Setting up for Drupal's Functional JavaScript tests
Favicon
Fix Memory Exhausted Issue in Laravel Tests
Favicon
Another way to create test module configuration
Favicon
Testando filas em projetos Laravel
Favicon
Checklist to Become Software Developer
Favicon
Chat : Test unitaire sur des méthodes privées
Favicon
Chat: Unit test of private methods
Favicon
Upgrading to Laravel 10, PHPUnit 10, and Pest 2
Favicon
Testing an external api using PHPUnit
Favicon
Installing PHP Unit
Favicon
Code coverage for PHPUnit in VSCode
Favicon
Behavior-Driven Testing with PHP and PHPUnit
Favicon
Run Laravel test multiple times
Favicon
Using PHP anonymous classes to test collection services
Favicon
Test coverage: did you set Xdebug's coverage mode?
Favicon
Using github actions to execute your PHP tests after every push
Favicon
Writing a basic Feature Test with PhpUnit in Laravel
Favicon
Fix Symfony tests with PHPUnit 10
Favicon
The most efficient way to debug problems with PHPUnit mocks
Favicon
Why I am unable to assert that method has been called on my mocked service?

Featured ones: