Bir test açıyorsunuz, CI’de bir haftadır kırmızı. Bu test, SendInvoiceEmail işini gönderiyor, ardından iki saniye bekliyor ve veritabanında gönderilen bir e-posta kaydını kontrol ediyor. Yerel ortamda geçiyor. CI üzerinde her beş denemede bir başarısız oluyor. Birinin sleep(2) ekleyerek testi güvenilir hale getirmeye çalıştığını anlıyoruz fakat bu, durumu daha da kötüleştirmiş.
Test, gerçek bir kuyruğa hitap ediyor. Bir işçi, işi alıp çalıştırmalı ve satır yazmadan önce doğrulama yapılmalı. Bu zamanlamayı kontrol edemezsiniz. Kuyruk sürücüsü, işçi polling aralığı ve CI makinesinin yükü, işin ne zaman çalışacağını belirliyor. Testiniz bir yarışa karşı doğrulama yapıyor.
Bir işi test etmek için gerçek bir kuyruğa ihtiyacınız yok. Bilmeniz gereken iki şey var: kod doğru bir işi mi gönderdi, ya da işin handle() metodu doğru mu çalışıyor. Bunlar farklı sorulardır, Laravel her biri için temiz bir araç sunuyor.
İki soru, asla bir test
İki soru, asla bir test
Yazmadan önce endişeleri ayırın. Bir işin iki tarafı vardır:
- Arayan taraf: Bir kontrolcü veya servis
SendInvoiceEmail::dispatch($invoice)kodunu çalıştırdı. Gönderimin doğru yükle, doğru kuyrukta gerçekleştiğini kanıtlamak istersiniz. - İşleyici taraf: İş verildiğinde,
handle()e-postayı gönderir ve faturayı işaretler. Bu mantığı izole olarak kanıtlamak istersiniz.
Bunları karıştırmak flue oluşturur. Hem göndereni hem beklemeyi test eden bir test, çerçevenin kuyruk sistemini test eder, sizin kodunuzu değil. Laravel zaten kendi kuyruk sistemini test ediyor. Siz bunu yapmak zorunda değilsiniz.
Queue::fake() gönderimi değil, işin doğruluğunu kanıtlar
Queue::fake() gönderimi değil, işin doğruluğunu kanıtlar
Queue::fake() kuyruk sürücüsünü hafızada bir casusa değiştirir. Hiçbir şey Redis’e itilmez. Hiçbiri çalışmaz. Her dispatch() kaydedilir, böylece daha sonra buna karşı doğrulama yapabilirsiniz.
namespace Tests\Feature;
use App\Jobs\SendInvoiceEmail;
use App\Models\Invoice;
use Illuminate\Support\Facades\Queue;
use Tests\TestCase;
class InvoiceCheckoutTest extends TestCase
{
public function test_checkout_queues_the_invoice_email(): void
{
Queue::fake();
$invoice = Invoice::factory()->create();
$this->post("/invoices/{$invoice->id}/pay")
->assertOk();
Queue::assertPushed(
SendInvoiceEmail::class,
fn (SendInvoiceEmail $job) =>
$job->invoice->is($invoice)
);
}
}
Closure, alınan işten doğru yükün çıktığını kanıtlamada önemli bir yer tutar. assertPushed ifadesi olmadan, bir sınıfın işinin gönderildiğini kanıtlar. Ancak onunla, doğru olanın gönderildiğini — doğru faturanın gönderildiğini kanıtlayabilirsiniz, sadece bir diğer fabrika satırından değil. Yük üzerinde doğrulama yapın, sadece sınıf üzerinde değil.
Karşılaştırma olumsuz doğrulamaları sizi dürüst tutar:
Queue::assertPushedOn('emails', SendInvoiceEmail::class);
Queue::assertNotPushed(ChargeCard::class);
Queue::assertPushed(SendInvoiceEmail::class, 1); // tam olarak bir kez
Queue::assertNothingPushed();
assertNotPushed genellikle atlanır. Ödenmiş bir fatura bir e-posta göndermeli ve tekrar kredi kartını şarj etmemelidir. Olması gereken şeylerin yanı sıra olmaması gereken şeylerin doğrulanması da bir testtir.
Bir nokta. Queue::fake() ayrıca zincirlenmiş ve gecikmeli işlerin çalışmasını durdurur, bu nedenle bir testte gönderimin arka planda gerçekten koşmasını istiyorsanız, bir işleyici testindesiniz demektir ve yanlış araca başvuruyorsunuz.
Bus::fake() batch ve zincirler içindir
Bus::fake() batch ve zincirler içindir
Queue::fake() basit gönderimlerinizi kapsar. Bus::batch() veya Bus::chain() kullandığınız anda, Bus::fake() kullanmaya geçin. Bu, Queue::fake() ile iyi modelleyemediğiniz gruplandırılmış ve zincirlenmiş çalışmaları kaydeder.
namespace Tests\Feature;
use App\Jobs\GenerateStatementChunk;
use Illuminate\Bus\PendingBatch;
use Illuminate\Support\Facades\Bus;
use Tests\TestCase;
class StatementBatchTest extends TestCase
{
public function test_month_end_dispatches_a_statement_batch(): void
{
Bus::fake();
$this->artisan('statements:run --period=2026-06');
Bus::assertBatched(
fn (PendingBatch $batch) =>
$batch->name === 'statements:2026-06'
&& $batch->jobs->count() === 4
&& $batch->jobs->first()
instanceof GenerateStatementChunk
);
}
}
PendingBatch, gönderilmeden önce size grubu verir: adı, kuyruğu ve içindeki işlerin koleksiyonu. Hiçbir çocuk çalıştırmadan fan-out’un şeklini doğrulayın. Zincirler için Bus::assertChained([First::class, Second::class]) sırayı kontrol eder.
Sadece bazı işler için sahte olmasını sağlayacak şekilde sahteyi kapsamlayabilirsiniz:
Bus::fake([GenerateStatementChunk::class]);
Bir komutun bir grubu göndermesi gerektiğinde faydalıdır ancak küçük bir muhasebe işinin yürütülmesini sağlarsınız.
Test the handler by calling handle() directly
Test the handler by calling handle() directly
Yukarıdaki arayan testler, işinizin mantığını çalıştırmaz. Bu, işleyicinin kendi testidir ve en temiz yol, kuyruk tamamen atlayarak metodu çağırmaktır.
Bir iş, düz bir PHP nesnesidir. Oluşturun, handle() çağrısını yapın, etkilere yönelik doğrulama yapın. Laravel’in konteyneri bağımlılıkları çözümler, bu nedenle handle()‘da bağımlılıkların tür ipucu verin ve app()->call() ile enjekte edelim:
namespace Tests\Feature;
use App\Jobs\SendInvoiceEmail;
use App\Models\Invoice;
use App\Mail\InvoiceMail;
use Illuminate\Support\Facades\Mail;
use Tests\TestCase;
class SendInvoiceEmailTest extends TestCase
{
public function test_handle_emails_the_customer(): void
{
Mail::fake();
$invoice = Invoice::factory()->create([
'status' => ,
]);
$job = new SendInvoiceEmail($invoice);
app()->call([$job, ]);
Mail::assertSent(
InvoiceMail::class,
fn (InvoiceMail $mail) =>
$mail->hasTo($invoice->customer_email)
);
$this->assertNotNull($invoice->refresh()->emailed_at);
}
}
Hiçbir kuyruk, hiçbir işçi, hiçbir uyku. İş, inline bir şekilde, işlemde, kesin olarak çalışır. Mail::fake(), Queue::fake()‘ın gönderimleri yakaladığı gibi, çıkışta yakalar. İki etkiyi doğruluyorsunuz: e-posta doğru adrese yollandı ve fatura damgalandı.
Bir bağımlılık enjekte edilecekse handle()‘da, $job->handle() yeterlidir. Metodun tür ipucu yoksa, app()->call()‘a ulaşmanız yeterlidir.
dispatchSync ne zaman kullanılır
dispatchSync ne zaman kullanılır
Arada bir orta yol var. Bazen işin gerçek gönderim yolu (middleware, ShouldQueue, bütün işler) boyunca çalışmasını istersiniz ama senkronize olarak çalışmasını, böylece testin bir işçiye beklemesini istemezsiniz. Bu dispatchSync() ile mümkündür:
public function test_paying_sends_the_email_inline(): void
{
Mail::fake();
$invoice = Invoice::factory()->create();
SendInvoiceEmail::dispatchSync($invoice);
Mail::assertSent(InvoiceMail::class);
}
dispatchSync işi mevcut süreçte çalıştırır, şu anda ve bitirdikten sonra geri döner. İş middleware’i hala geçerlidir, bu nedenle handle() doğrudan çağrıldığında ki bu olumlu bir sonuçtur. Middleware, test ettiğiniz şeyin bir parçasıdır — hız sınırlaması, WithoutOverlapping, şifreleme. Logic ile birlikte hiçbir engel olmadan iş mantığını istiyorsanız doğrudan handle() çağrısını yapabilirsiniz.
Bir şey dispatchSync‘in yapmadığıdır: başarısız olduğunda tekrar deneme yapmaz veya $backoff‘a saygı göstermez. Bu, işçi davranışıdır. Testinizin tekrar deneme mantığına ihtiyacı varsa, bu, işin retryUntil() veya $tries konfigürasyonuna karşı bir test olmalıdır, uçtan uca bir çalışma değil.
Neden bir ünitelik testte gerçek bir kuyruk kötü bir kokudur
Neden bir ünitelik testte gerçek bir kuyruk kötü bir kokudur
Bir adım geri atın ve bu anti-modeli isimlendirin. Redis’e iten ve bir işçi bekleyen bir test, üç şeyi birleştirir; kodunuzu, kuyruk sürücüsünü ve duvar-saat zamanlamasını. Başarısız olduğunda hangi üçünden birinin kırıldığını söyleyemezsiniz. Bu, bir flue testinin tanımıdır — bir neden ile bir başarısızlık arasında uygun bir eşleşme yoktur.
Dar, dürüst bir istisna vardır. Uyanık bir işçi başlatmak ve Horizon yapılandırmanızı, serileştiricinizi ve bağlantınızı verimli bir şekilde doğrulamak için entegrasyon testi anlamına gelen bir tane var. Bunu kontrol altında tutmak için bir veya iki tane olmalı, zamanlama üzerinden çalıştırılmalı. Üç yüz tane ünitelik test ve her biri küçük bir yarışma şansına sahip her biri, bu testler için sıfır puan getirir.
QUEUE_CONNECTION=sync kısayolu phpunit.xml içinde ilişkili bir tuzaktır. Bu, her gönderimi inline olarak çalıştırır
ve bu da zahmetli bir durumdur, ancak bu sizin gönderen testlerinizin yöneticiyi sessizce çalıştırır. Sadece “iş kuyruğa alındı” kanıtlaması gereken bir kontrolcü testi, e-posta gönderimini çalıştırır, e-posta alıcısını etkiler ve ne olursa olsun, işleyici etkiler. Test ortamından sync çıkarmak ve istediğiniz her test için Queue::fake() uygulamak en iyisidir. Açık olan her zaman çevre birimin önündedir.
Ölçeklenebilir biçim
Ölçeklenebilir biçim
Büyüyen bir iş setini hızlı ve dürüst tutmak için üç alışkanlık:
- Gönderen testler
Queue::fake()/Bus::fake()kullanır ve gönderim üzerine kanıt sağlar — sınıf, yük, kuyruk, sayım. Asla işleyiciyi çalıştırmaz. - İşleyici testleri
handle()doğrudan arar (middleware önemli olduğundadispatchSync()) ileMail::fake(),Http::fake(),Event::fake()için çıkışta etkiler. Gerçek bir aracıya dokunmayacaklardır. - Birkaç entegrasyon testi Uzanma için bir gerçek işçi başlatır ve kabloları korur. Böyle adlandırılır, ünitelik setinden ayrı olarak çalıştırılır.
Bunları yerine getirirseniz, iş testleriniz CI’de zaman aşımına uğramayı durdurur, sleep() gereksinimini ortadan kaldırır ve tam olarak hangi durumların kırıldığını gösterir.
Eğer bu faydalı olduysa
Eğer bu faydalı olduysa
Bu testlerin temiz olma nedeni, gerçek işin (fatura gönderimi, beyan oluşturma) küçük bir işlev olmasıdır ve kuyruğun içerisinde ne yaptığını bilmez. O operasyon sizin alanınızda yaşar ve iş sınıfı onu çağıran ince bir adaptör olur; testler doğal bir şekilde bölünür: işleyici testi alan yöntemlerini, arayan testi gönderimi simüle eder. Kenar ile çekirdek arasındaki bu ayrım, Decoupled PHPNin tam olarak ne hakkında olduğunu: çerçevenin kuyruk sıralanışını sınırda tutmak böylece test edilecek şey, çerçeve olmadan ulaşılabilir kalır.
Mevcut, Kindle, Ciltli ve Sert Kapak sürümleri mevcuttur. Şu anda İngilizce, Almanca ve Japonca baskılara sahip – Portekizce ve İspanyolca sürümler de geliyor.
Kaynak: Orijinal Makale



