Queues, “bende çalıştı” klişesinin prodüksiyon kazalarına dönüşmesiyle yoğun bir mühendislik disiplini gerektirir.
Emails arka planda gönderilir.
Faturalar asenkron olarak oluşturulur.
Dış API’lere istek döngüsü dışında ulaşılır.
Veri senkronizasyonu, aktif olarak izlenmeyen işçilerde çalışır.
Laravel, queue kullanımını son derece kolay hale getirir. Sadece ShouldQueue ekleyin, işi yayınlayın, tamam.
Onları uygun şekilde test etmek ise farklı bir hikaye.
Çoğu geliştirici şu aşamada kalır:
Bus::fake();
Bus::assertDispatched(SyncOrderJob::class);
Bu, kabloların kontrolünü sağlar. Ancak davranışı doğrulamaz.
Bu makale, Laravel işlerini doğru bir şekilde nasıl test edeceğimiz üzerine odaklanıyor — bunun içinde idempotans, hata yönetimi ve yeniden deneme güvenliği de var — gerçekçi bir örnek ile.
Senaryo: Bir Siparişi Dış API’ye Senkronize Etmek
Senaryo: Bir Siparişi Dış API’ye Senkronize Etmek
Diyelim ki bir sipariş modelimiz var ve bu model, alışveriş işlemi sonrasında dış bir sisteme senkronize edilmesi gerekiyor.
İş
namespace App\Jobs;use App\Models\Order;
use App\Services\OrderApiClient;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;class SyncOrderJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, SerializesModels; public int $tries = 3; public function __construct(
public Order $order
) {} public function handle(OrderApiClient $client): void
{
if ($this->order->synced_at !== null) {
return; // idempotans koruması
} $client->send([
'id' => $this->order->id,
'total' => $this->order->total,
]); $this->order->update([
'synced_at' => now(),
]);
}
}
Şimdi birkaç önemli konuya sahipiz:
- İş, iki kere senkronize edilmemeli.
- Dış bir API istemcisine bağımlıdır.
- Yeniden denenebilir.
- Kalıcı durumu değiştirir.
Şimdi bunu doğru bir şekilde test edelim.
1. Yayın Testi (Entegrasyon Seviyesinde)
1. Yayın Testi (Entegrasyon Seviyesinde)
Bu bir özellik testine aittir.
use Illuminate\Support\Facades\Bus;
use App\Jobs\SyncOrderJob;
public function test_order_dispatches_sync_job()
{
Bus::fake();
$order = Order::factory()->create();
$order->markAsPaid(); // bu işin yayınlandığını varsayın
Bus::assertDispatched(SyncOrderJob::class, function ($job) use ($order) {
return $job->order->is($order);
});
}
Bu iyi. Gereklidir.
Ancak yetersiz.
Bu, handle() işlevini hiç test etmez.
2. İş Mantığını Doğrudan Test Etmek (Birim Seviyesinde)
2. İş Mantığını Doğrudan Test Etmek (Birim Seviyesinde)
Şimdi işin kendisini test ediyoruz.
Biz bus’ı sahtelemiyoruz.
İşi oluşturuyor ve handle()’ı doğrudan çağırıyoruz.
Dış API’yi Taklit Etmek
Dış API’yi Taklit Etmek
use App\Services\OrderApiClient;
use Mockery;
public function test_it_calls_external_api_when_not_synced()
{
$order = Order::factory()->create([
'synced_at' => null,
]);
$mock = Mockery::mock(OrderApiClient::class);
$mock->shouldReceive('send')
->once()
->with(Mockery::on(fn ($payload) => $payload['id'] === $order->id));
$job = new SyncOrderJob($order);
$job->handle($mock);
}
Bu gerçek davranışı doğrular.
Biz Laravel’i test etmiyoruz.
Biz, iş mantığımızı test ediyoruz.
3. İdempotansı Test Etmek
3. İdempotansı Test Etmek
Queue’lar otomatik olarak yeniden dener. Eğer işiniz idempotent değilse, tekrarlayan yan etkiler yaratırsınız.
public function test_it_does_not_call_api_if_already_synced()
{
$order = Order::factory()->create([
'synced_at' => now(),
]);
$mock = Mockery::mock(OrderApiClient::class);
$mock->shouldNotReceive('send');
$job = new SyncOrderJob($order);
$job->handle($mock);
}
Eğer bu test başarısız olursa, üretim sisteminiz sonunda işi iki kere yapacaktır.
4. Yeniden Deneme Güvenliğini Test Etmek
4. Yeniden Deneme Güvenliğini Test Etmek
API çağrısı ile veri tabanı güncellemesi arasında bir sorun olabileceğini düşünün.
Eğer iş, API’yi çağırdıktan sonra ama siparişi senkronize etmeden çöküyorsa, yeniden deneme işlemi yine gönderilecektir.
Daha güvenli bir model:
public function handle(OrderApiClient $client): void
{
if ($this->order->synced_at !== null) {
return;
} $this->order->update([
'synced_at' => now(),
]); $client->send([
'id' => $this->order->id,
'total' => $this->order->total,
]);
}
Artık yan etkilerden önce durum değişiklikleri var.
Şimdi bir hatayı simüle edelim.
public function test_it_retries_safely()
{
$order = Order::factory()->create([
'synced_at' => null,
]);
$mock = Mockery::mock(OrderApiClient::class);
$mock->shouldReceive('send')
->once()
->andThrow(new RuntimeException());
$job = new SyncOrderJob($order);
try {
$job->handle($mock);
} catch (RuntimeException $e) {
// bekleniyor
}
$order->refresh();
$this->assertNotNull($order->synced_at);
}
Artık yeniden deneme olsa bile, idempotans koruması çift yürütmeyi engeller.
Bu teorik değil. İşte çift faturaların başına gelenler.
5. Diğer İşleri Yayınlayan İşlerin Testi
5. Diğer İşleri Yayınlayan İşlerin Testi
Zincirlenmiş veya iç içe geçmiş işler karmaşıklık getirir.
ProcessPaymentJob::dispatch($order);
SendInvoiceJob::dispatch($order);
Bunu doğru bir şekilde test etmek için:
Bus::fake();
$job = new ProcessPaymentJob($order);
$job->handle($paymentService);
Bus::assertDispatched(SendInvoiceJob::class);
Farkı not edin:
- handle()’ı doğrudan test ediyoruz.
- Sadece iç içe yayınlamayı doğrulamak için bus’ı sahte yapıyoruz.
Bu ayrım kritiktir.
6. Geri Sayım ve Yeniden Deneme Konfigürasyonunu Test Etmek
6. Geri Sayım ve Yeniden Deneme Konfigürasyonunu Test Etmek
Laravel, yapılandırma için izin verir:
public int $tries = 5;
public function backoff(): array
{
return [10, 30, 60];
}
Yapılandırmayı açıkça test edebilirsiniz:
public function test_job_has_correct_retry_settings()
{
$job = new SyncOrderJob(Order::factory()->make());
$this->assertEquals(3, $job->tries);
}
Gösterişli değil — ama yapılandırma hataları gerçek kesintilere yol açabilir.
Tehlikeli Anti-Model
Tehlikeli Anti-Model
Bu yaygındır:
Queue::fake();
SyncOrderJob::dispatch($order);
Queue::assertPushed(SyncOrderJob::class);
Bu test, aşağıdaki durumlarda geçer:
- İş hemen hata verirse.
- API istemcisi bozuksa.
- Mantık ters ise.
- Idempotans eksikse.
- İş, siparişi yanlışlıkla silerse.
Yayınlamayı değil, davranışı test ettiniz.
Bus::fake() Ne Zaman Kullanılır?
Bus::fake() Ne Zaman Kullanılır?
Bus::fake() kullanın:
- İşleri yayınlayan kontrolcüler veya hizmetleri test ederken.
- Orkestrasyonu doğrularken.
- Belirli koşullar altında bir işin sıraya alınmasını sağlar.
Bus::fake() kullanmayın:
- İş mantığını test ederken.
- Dış entegrasyon davranışını test ederken.
- Hata yönetimini test ederken.
İş testlerinde, instantiate edip handle()’ı doğrudan çağırın.
İleri Düzey: Testlerde İşleri Senkron Çalıştırmak
İleri Düzey: Testlerde İşleri Senkron Çalıştırmak
Laravel şunu sağlar:
config(['queue.default' => 'sync']);
Bu, işleri hemen çalıştırır.
Özellik testleri için faydalıdır, burada:
- Tam davranışın uygulanmasını sağlarsınız.
- Queue altyapısına önem vermezsiniz.
Ancak dikkatli olun:
Eğer senkron çalıştırmaya her yerde bağımlı kalırsanız, yarış koşulları veya yeniden deneme sorunlarını atlayabilirsiniz.
Test Edilebilir İşler İçin Tasarım İlkeleri
Test Edilebilir İşler İçin Tasarım İlkeleri
Eğer işiniz test edilmesi zor ise, genellikle kötü tasarlanmıştır.
İyi Laravel işler:
- Hizmetleri handle() aracılığıyla enjekte eder.
- Konstrüktörlerini hafif tutar.
- __construct içinde ağır mantıktan kaçınır.
- Statik hizmet çağrılarından kaçınır.
- İdempotans tasarım gereği vardır.
- Yüksek sesle hata verirler.
Kötü Laravel işler:
- handle içinde doğrudan Http::post() çağrısı yaparlar.
- Modelleri statik olarak sorgularlar, soyutlama yapmazlar.
- Genel durumu değiştirirler.
- İstisnaları sessizce yutma davranışları vardır.
Test, mimari kalitesini açığa çıkarır.
Son Düşünceler
Son Düşünceler
Queues, üç yeni karmaşıklık boyutu getirir:
- Zaman (çalıştırma gecikmeli)
- Başarısızlık (otomatik yeniden denemeler)
- Eşzamanlılık (birden fazla işçi)
Laravel işlerimizi doğru bir şekilde test etmek demektir:
- Özellik düzeyinde dağıtım testi.
- Direct olarak handle()’ı birim düzeyinde test etme.
- Dış hizmetleri taklit etme.
- İdempotansı doğrulama.
- Hata simülasyonu yapma.
- Yeniden deneme güvenliğini açıkça düşünme.
Queues, uygulamanızı ölçeklendirir.
Testler, arka plan işleme güvenilirliği sağlar.
Kaynak: Orijinal Makale
- Senaryo: Bir Siparişi Dış API’ye Senkronize Etmek
- 1. Yayın Testi (Entegrasyon Seviyesinde)
- 2. İş Mantığını Doğrudan Test Etmek (Birim Seviyesinde)
- Dış API’yi Taklit Etmek
- 3. İdempotansı Test Etmek
- 4. Yeniden Deneme Güvenliğini Test Etmek
- 5. Diğer İşleri Yayınlayan İşlerin Testi
- 6. Geri Sayım ve Yeniden Deneme Konfigürasyonunu Test Etmek
- Tehlikeli Anti-Model
- Bus::fake() Ne Zaman Kullanılır?
- İleri Düzey: Testlerde İşleri Senkron Çalıştırmak
- Test Edilebilir İşler İçin Tasarım İlkeleri
- Son Düşünceler


