ix
ix

informatix

EN version Porozmawiajmy o projekcie
Strona główna / Blog / Testowanie kolejek w Laravel — Queue::fake i Bus::fake [2026...
• 8 min czytania •

Testowanie kolejek w Laravel — Queue::fake i Bus::fake [2026]

Kolejki to jedyny fragment aplikacji, którego nie widać w testach HTTP — literówka w dispatchu przejdzie całe CI. Queue::fake i Bus::fake sprawdzają dyspozycję, handle() testuje logikę. Kompletny zestaw asercji i pułapki.

Kolejki to jedyny fragment aplikacji, którego nie widać w testach HTTP — jeśli nie sprawdzisz, że żądanie zakolejkowało właściwe zadanie z właściwym payloadem, literówka w nazwie klasy albo brakujący dispatch() przejdą cały CI i wyjdą na produkcji. Laravel daje na to dwie fasady: Queue::fake() i Bus::fake(). Ten poradnik pokazuje kompletny zestaw asercji, testowanie logiki samych zadań i pułapki, które sprawiają, że testy kolejek kłamią.

Najważniejsze: Najważniejsze — Queue::fake() w teście feature sprawdza dyspozycję (że zadanie zostało zakolejkowane i z jakim payloadem), nie wykonuje zadania. Logikę zadania testujesz osobno: bezpośrednim wywołaniem handle() albo testem z prawdziwym sterownikiem sync. Bus::fake() działa analogicznie dla batchy i łańcuchów.

Dwie warstwy testowania kolejek

Testy kolejek dzielą się na dwie warstwy i mieszanie ich to najczęstszy błąd

  1. Dyspozycja — czy aplikacja we właściwym momencie kolejkuje właściwe zadanie? Testujesz Queue::fake() + asercje. Szybkie, nie dotykają kolejki.
  2. Wykonanie — czy zadanie robi to, co powinno? Testujesz bezpośrednio logikę zadania (wywołanie handle() na obiekcie), bez fasad i bez kolejki.

Test, który próbuje robić obie rzeczy naraz (fake + oczekiwanie efektów), zawsze kłamie: Queue::fake() sprawia, że zadanie nigdy się nie wykona, więc żadne efekty nie powstaną.

Queue::fake — asercje dyspozycji

  • Podstawowy test feature: żądanie HTTP ma zakolejkować zadanie synchronizacji.
use App\Jobs\SyncOrderToErp;
use Illuminate\Support\Facades\Queue;

public function test_order_creation_dispatches_sync_job(): void
{
    Queue::fake();

    $this->post('/orders', [
        'product_id' => 1,
        'quantity' => 2,
    ])->assertRedirect();

    Queue::assertPushed(SyncOrderToErp::class);
}

Kompletny zestaw asercji, które przydają się w praktyce

// Zadanie zakolejkowane z konkretnym payloadem
Queue::assertPushed(SyncOrderToErp::class, function ($job) {
    return $job->order->id === 42;
});

// Zadanie trafiło do właściwej kolejki (priorytet)
Queue::assertPushedOn('high', SendInvoiceEmail::class);

// Zadanie NIE zostało zakolejkowane (np. walidacja biznesowa)
Queue::assertNotPushed(SyncOrderToErp::class);

// Dokładnie N zadań danego typu
Queue::assertPushed(SyncOrderToErp::class, 1);

// Nic nie poszło do kolejki
Queue::assertNothingPushed();

Warto testować payload, nie tylko samą klasę — $job->order->id === 42 łapie błędy, w których zadanie jest kolejkowane z pustym albo złym obiektem. Dwa testy: „zakolejkowano" i „zakolejkowano z właściwymi danymi" kosztują sekundy, a oszczędzają debugowania na produkcji.

Bus::fake — batche i łańcuchy

Gdy aplikacja używa Bus::batch() albo Bus::chain(), Queue::fake() ich nie obejmuje — potrzebujesz Bus::fake()

use App\Jobs\ImportRow;
use Illuminate\Support\Facades\Bus;

public function test_import_dispatches_batch(): void
{
    Bus::fake();

    $this->post('/imports', ['file' => $file])->assertRedirect();

    Bus::assertBatched(function ($batch) {
        return $batch->name === 'Import produktów'
            && $batch->jobs->count() === 100;
    });
}

Dostępne asercje dla batchy i łańcuchów

  • Bus::assertBatched(fn ($batch) => ...) — batch zakolejkowany z warunkiem (nazwa, liczba zadań, kolejka).
  • Bus::assertChained([FetchInvoice::class, GenerateInvoicePdf::class]) — łańcuch w dokładnie takiej kolejności.
  • Bus::assertDispatched(SyncPrices::class) — pojedyncze zadanie przez Bus::dispatch().
  • Bus::assertNothingBatched() — nic nie poszło jako batch.

Testowanie logiki samego zadania

Właściwą pracę zadania testujesz bez kolejki — najprościej wywołaniem handle() na przygotowanym obiekcie

public function test_job_marks_order_as_synced(): void
{
    $order = Order::factory()->create(['erp_id' => null]);
    $erp = Mockery::mock(ErpClient::class);
    $erp->shouldReceive('push')->once()->andReturn('ERP-123');

    (new SyncOrderToErp($order))->handle($erp);

    $this->assertSame('ERP-123', $order->fresh()->erp_id);
}

Tak testujesz pełną logikę — łącznie z wyjątkami, retry i idempotencją — bez dotykania kolejki i bez Queue::fake(). Dwie zalety: test jest szybki (brak bootstrapu workera) i sprawdza efekty, a nie tylko wywołanie.

Do testów integracyjnych z prawdziwym cyklem kolejki użyj sterownika sync — zadanie wykona się natychmiast, w tym samym procesie:

// phpunit.xml: <env name="QUEUE_CONNECTION" value="sync"/>
public function test_full_flow_with_sync_queue(): void
{
    $this->post('/orders', [...])->assertRedirect();

    $this->assertDatabaseHas('orders', ['erp_id' => 'ERP-123']);
}

Wskazówka: Trzy poziomy testów kolejek: (1) Queue::fake() + asercje dyspozycji — najtańsze, najszersze; (2) test handle() z mockami — logika zadania; (3) test ze sterownikiem sync — pełny przepływ end-to-end w procesie. Nie każdy projekt potrzebuje wszystkich trzech, ale warstwa 1 powinna istnieć zawsze.

Faking wybranych zadań

Czasem chcesz, żeby jedno zadanie wykonało się naprawdę, a inne były sfakowane — np. testujesz wysyłkę maila, ale nie chcesz realnej synchronizacji z ERP:

Queue::fake([SyncOrderToErp::class]);   // fake tylko tego zadania

Queue::fake()->except([SendInvoiceEmail::class]);   // wszystko oprócz tego

Pierwszy wariant przydaje się, gdy przechodzisz z synchronicznego wykonania na kolejki — możesz sfakować tylko nowe zadania, a resztę zostawić działającą.

Pułapki, które sprawiają, że testy kłamią

  • Queue::fake() + oczekiwanie efektów — zadanie nigdy się nie wykona; jeśli test „przechodzi", sprawdza coś innego, niż myślisz.
  • Fake bez asercji — Queue::fake() bez assertPushed() nie testuje niczego. Fake bez asercji to fałszywe poczucie bezpieczeństwa.
  • QUEUE_CONNECTION=sync w testach bez świadomości — wtedy Queue::fake() nie jest potrzebne, ale zadania wykonują się w procesie; błędy w zadaniach wywalają testy HTTP (często pożądane, ale trzeba o tym wiedzieć).
  • Testowanie prywatnych metod zadania — testuj handle(), nie implementację. Refaktoryzacja wewnątrz zadania nie powinna łamać testów.
  • Brak testu retry — sprawdź, że zadanie po wyjątku faktycznie wraca do kolejki (test z expectException i ponownym handle()), bo literówka w $tries też jest błędem.

Pest czy PHPUnit?

Oba działają identycznie — Queue::fake() to fasada, nie zależy od frameworka testowego. W Pesta zapis jest zwięźlejszy

it('kolejkuje synchronizację po utworzeniu zamówienia', function () {
    Queue::fake();

    $this->post('/orders', ['product_id' => 1])->assertRedirect();

    Queue::assertPushed(SyncOrderToErp::class);
});

Wybór między Pest a PHPUnit opisuję w kontekście całego projektu — oba podejścia są w użyciu produkcyjnym i nie ma tu złej decyzji. Ważne, żeby warstwa dyspozycji była pokryta w CI razem z resztą testów.

Najczęściej zadawane pytania

Czym różni się Queue::fake() od Bus::fake()?

Queue::fake() przechwytuje klasyczne kolejkowanie (dispatch(), Queue::push()) — pokrywa większość przypadków. Bus::fake() obsługuje batch i łańcuchy (Bus::batch(), Bus::chain()), których Queue::fake() nie przechwytuje. W nowoczesnych aplikacjach używa się obu, zależnie od tego, czego dotyczy test.

Czy Queue::fake() wykonuje zadania?

Nie — fake podmienia sterownik kolejki na atrapę, która tylko zapisuje, co zostało zakolejkowane. Zadania nie wykonują się w ogóle. Dlatego efekty zadania testujesz osobno (bezpośrednie handle() albo sterownik sync), a fake służy wyłącznie do asercji dyspozycji.

Jak przetestować, że zadanie NIE zostało zakolejkowane?

Queue::assertNotPushed(SyncOrderToErp::class) po wykonaniu żądania. Scenariusz: walidacja biznesowa blokuje operację (np. zamówienie bez wymaganego pola), więc zadanie nie powinno trafić do kolejki. Warto też sprawdzić Queue::assertNothingPushed() dla całkowicie zablokowanych ścieżek.

Jak testować zadania z retry i backoffem?

Testuj logikę przez handle(): przygotuj stan, w którym pierwsze wywołanie rzuca wyjątek, i sprawdź, że stan po nim jest spójny (idempotencja). Konfigurację retry ($tries, $backoff) sprawdzisz asercją na właściwościach zadania — $this->assertSame(3, (new SyncOrderToErp($order))->tries) — albo w teście integracyjnym ze sterownikiem sync.

Czy testy kolejek działają z Laravel Horizon?

Horizon jest warstwą nad kolejką — nie wpływa na Queue::fake(). Testy działają identycznie, niezależnie od tego, czy produkcja używa Horizon, Supervisora czy sterownika database. Sam panel Horizon testujesz osobno (dostęp, autoryzacja przez bramkę viewHorizon), a nie przez fake.


👉 Zadania znikają bez śladu, a testy „przechodzą"? Prawdopodobnie warstwa dyspozycji nie jest pokryta. Opisz projekt na kontakt@informatix.com.pl lub przez formularz — dopiszemy testy kolejek do istniejącego pipeline'u.

📖 Zobacz również: Kolejki w Laravel i Laravel Horizon — poradnik, queue:work vs queue:listen, job batching, chains i rate limiting oraz usługa Laravel development.

Darmowy PDF

Checklista wymagań do wyceny aplikacji

12 pytań kontrolnych, które warto przygotować przed rozmową z wykonawcą — szybsza wycena, mniej ukrytych kosztów.

Powiązane wpisy

09.10.2026

Laravel Horizon i kolejki w Laravel — kompletny poradnik [2026]

Kolejki w Laravel przenoszą ciężkie operacje poza cykl żądania HTTP, a Laravel Horizon daje monitoring i autoskalowanie workerów. Kompletny poradnik: wybór sterownika, konfiguracja produkcyjna, retry i idempotencja, testy, koszty wdrożenia.

Czytaj dalej

21.10.2026

Job batching, chains i rate limiting w Laravel — wzorce [2026]

Import 10 000 wierszy jako batch z paskiem postępu, faktura → PDF → mail jako chain i rate limiting, który chroni API partnera przed kaskadą 429. Trzy wzorce, które odróżniają „używam kolejek" od „używam kolejek świadomie".

Czytaj dalej