Laravel Testing 2026: Pest, Mocking ve Teknik Mülakat Soruları

Laravel uygulamalarında Pest ile test yazma, facade mocking, mimari testler ve mutation testing konularını kapsamlı bir şekilde inceleyen rehber. Teknik mülakatlara hazırlık için pratik kod örnekleri ve sık sorulan sorular.

Laravel Testing 2026 - Pest framework ile test yazma ve mocking teknikleri

Laravel ekosisteminde test yazmak, modern PHP geliştirme süreçlerinin vazgeçilmez bir parçası haline gelmiştir. 2026 yılında Pest, Laravel topluluğunda standart test framework'ü olarak konumunu sağlamlaştırmış ve PHPUnit'in sunduğu tüm özellikleri daha okunabilir, daha az boilerplate kod ile sunmaktadır. Bu makalede Laravel projelerinde profesyonel düzeyde test yazma teknikleri, mocking stratejileri ve teknik mülakatlarda karşılaşılabilecek sorular ele alınmaktadır.

Test Driven Development (TDD) yaklaşımı, yazılım kalitesini artırmanın en etkili yollarından biridir. Laravel'in sunduğu test altyapısı, HTTP testlerinden veritabanı işlemlerine, queue job'larından mail gönderimlerine kadar uygulamanın her katmanını test etmeye olanak tanımaktadır. Pest framework'ü ile bu testler hem daha okunabilir hem de daha hızlı yazılabilir hale gelmektedir.

Bu rehberde feature testlerden unit testlere, facade mocking'den Mockery ile harici servis simülasyonuna kadar geniş bir yelpazede Laravel test pratikleri incelenmektedir. Ayrıca mimari testler ve mutation testing gibi ileri düzey konular da ele alınarak, teknik mülakatlara kapsamlı bir hazırlık sunulmaktadır.

Pest Neden PHPUnit'e Tercih Edilmeli?

Pest, PHPUnit üzerine inşa edilmiş modern bir test framework'üdür. Closure tabanlı syntax'ı sayesinde test dosyaları daha kısa ve okunabilir olmaktadır. it() ve expect() fonksiyonları ile doğal dil akışına yakın testler yazılabilmektedir. Higher-order testler, dataset'ler ve architecture testleri gibi özellikler Pest'i Laravel projeleri için ideal bir seçim haline getirmektedir. PHPUnit'in tüm assertion'ları kullanılabilirken, Pest'in expectation API'si daha ekspresif bir alternatif sunmaktadır.

Pest'i Laravel Projesinde Yapılandırma

Pest kurulumu sonrasında tests/Pest.php dosyası projenin test yapılandırmasını merkezi olarak yönetmektedir. Bu dosyada base test class'ı, trait'ler ve test dizinleri tanımlanmaktadır.

tests/Pest.phpphp
use Illuminate\Foundation\Testing\RefreshDatabase;

pest()
    ->extend(Tests\TestCase::class)
    ->use(RefreshDatabase::class)
    ->in('Feature');

Bu yapılandırma ile tests/Feature dizinindeki tüm testler TestCase sınıfından extend edecek ve RefreshDatabase trait'ini kullanacaktır. Her test çalışmadan önce veritabanı migration'ları çalıştırılacak ve test sonrasında veritabanı sıfırlanacaktır. Bu yaklaşım, testler arasında veri izolasyonu sağlamaktadır.

Feature Testleri: HTTP Endpoint Doğrulama

Feature testleri, uygulamanın HTTP katmanını test etmek için kullanılmaktadır. Kullanıcı kayıt, giriş, CRUD işlemleri gibi endpoint'lerin beklenen davranışları bu testlerle doğrulanmaktadır.

tests/Feature/UserRegistrationTest.phpphp
use App\Models\User;

it('registers a new user with valid data', function () {
    $response = $this->postJson('/api/register', [
        'name' => 'Jane Doe',
        'email' => 'jane@example.com',
        'password' => 'SecurePass123!',
        'password_confirmation' => 'SecurePass123!',
    ]);

    $response->assertStatus(201)
        ->assertJsonStructure(['user' => ['id', 'name', 'email']]);

    expect(User::where('email', 'jane@example.com')->exists())->toBeTrue();
});

Bu testte kullanıcı kayıt endpoint'ine geçerli veriler gönderilmekte ve yanıtın 201 status kodu ile döndüğü doğrulanmaktadır. Ayrıca JSON yapısının beklenen formatta olduğu ve kullanıcının veritabanına kaydedildiği kontrol edilmektedir. postJson() metodu, Content-Type: application/json header'ı ile istek göndermektedir.

Unit Testler: İzole İş Mantığı Testi

Unit testler, tek bir sınıf veya metodun davranışını izole bir şekilde test etmek için kullanılmaktadır. Harici bağımlılıklar mock'lanarak, test edilen birimin doğru çalıştığı garanti altına alınmaktadır.

tests/Unit/PriceCalculatorTest.phpphp
use App\Services\PriceCalculator;

describe('PriceCalculator', function () {
    it('applies a percentage discount correctly', function () {
        $calculator = new PriceCalculator();

        // 20% off a 150.00 base price
        $result = $calculator->applyDiscount(150.00, 20);

        expect($result)->toBe(120.00);
    });

    it('rejects negative discount values', function () {
        $calculator = new PriceCalculator();

        expect(fn () => $calculator->applyDiscount(100.00, -5))
            ->toThrow(InvalidArgumentException::class);
    });
});

describe() bloğu, ilgili testleri gruplamak için kullanılmaktadır. İlk testte indirim hesaplamasının doğruluğu kontrol edilirken, ikinci testte negatif indirim değerlerinin exception fırlattığı doğrulanmaktadır. toThrow() expectation'ı, belirli bir exception türünün fırlatılmasını beklemektedir.

Laravel mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Laravel Facade Mocking: Mail, Queue, Notifications

Laravel'in facade'ları, testlerde kolayca mock'lanabilmektedir. Mail::fake(), Queue::fake(), Notification::fake() gibi metodlar, gerçek işlemlerin yapılmasını engelleyerek test ortamında güvenli bir şekilde assertion yapılmasına olanak tanımaktadır.

tests/Feature/OrderProcessingTest.phpphp
use App\Models\Order;
use App\Models\User;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Queue;
use App\Mail\OrderConfirmation;
use App\Jobs\ProcessPayment;

it('dispatches payment job and sends confirmation email', function () {
    // Fake both Mail and Queue facades
    Mail::fake();
    Queue::fake();

    $user = User::factory()->create();
    $order = Order::factory()->for($user)->create([
        'total' => 99.99,
        'status' => 'pending',
    ]);

    // Act: confirm the order via HTTP
    $this->actingAs($user)
        ->postJson("/api/orders/{$order->id}/confirm")
        ->assertOk();

    // Assert: payment job was dispatched with correct amount
    Queue::assertPushed(ProcessPayment::class, function ($job) use ($order) {
        return $job->order->id === $order->id
            && $job->order->total === 99.99;
    });

    // Assert: confirmation email was sent to the user
    Mail::assertSent(OrderConfirmation::class, function ($mail) use ($user) {
        return $mail->hasTo($user->email);
    });
});

Bu testte sipariş onaylandığında hem ödeme job'ının queue'ya eklendiği hem de onay mailinin gönderildiği doğrulanmaktadır. Closure içinde yapılan kontroller, job ve mail'in doğru parametrelerle oluşturulduğunu garanti etmektedir. actingAs() metodu, belirli bir kullanıcı olarak authenticate edilmiş istek yapmayı sağlamaktadır.

Mockery ile İleri Düzey Mocking: Harici Servisler

Harici API'ler ve servisler, Mockery kütüphanesi ile mock'lanabilmektedir. Bu yaklaşım, testlerin harici servislere bağımlılığını ortadan kaldırarak hızlı ve güvenilir testler yazmayı mümkün kılmaktadır.

tests/Feature/PaymentGatewayTest.phpphp
use App\Services\PaymentGateway;
use App\Services\StripeClient;

it('charges the customer through the payment gateway', function () {
    // Create a mock of the Stripe client
    $stripeClient = Mockery::mock(StripeClient::class);
    $stripeClient->shouldReceive('charge')
        ->once()
        ->with('cus_abc123', 5000, 'usd')
        ->andReturn(['status' => 'succeeded', 'id' => 'ch_xyz']);

    // Bind the mock in the container
    $this->app->instance(StripeClient::class, $stripeClient);

    $gateway = app(PaymentGateway::class);
    $result = $gateway->processCharge('cus_abc123', 50.00);

    expect($result['status'])->toBe('succeeded');
});

Mockery ile oluşturulan mock nesne, shouldReceive() ile beklenen metod çağrılarını tanımlamaktadır. once() metodun tam olarak bir kez çağrılmasını, with() doğru parametrelerle çağrılmasını, andReturn() ise döndürülecek değeri belirlemektedir. Mock nesne Laravel container'a bind edilerek, dependency injection ile kullanılmaktadır.

Mimari Testler: Proje Kurallarını Zorunlu Kılma

Pest'in architecture testing özelliği, projedeki yapısal kuralların otomatik olarak denetlenmesini sağlamaktadır. Controller'ların doğrudan Eloquent kullanmaması, service sınıflarının final olması gibi kurallar test sürecinde kontrol edilebilmektedir.

tests/Architecture/ArchitectureTest.phpphp
arch('controllers do not use Eloquent directly')
    ->expect('App\Http\Controllers')
    ->not->toUse('Illuminate\Database\Eloquent');

arch('services are final classes')
    ->expect('App\Services')
    ->toBeFinal();

arch('no debugging functions in production code')
    ->expect(['dd', 'dump', 'var_dump', 'ray'])
    ->not->toBeUsed();

Bu testler CI/CD pipeline'ında çalıştırılarak, kod review sürecinde gözden kaçabilecek yapısal sorunların önüne geçilmektedir. İlk test controller'ların repository veya service pattern kullanmasını zorunlu kılmakta, ikinci test service sınıflarının extend edilemeyeceğini garanti etmekte, üçüncü test ise debug fonksiyonlarının production kodunda bulunmamasını sağlamaktadır.

Pest ayrıca hazır preset'ler sunmaktadır:

tests/Architecture/LaravelPresetTest.phpphp
arch()->preset()->laravel();
arch()->preset()->security();
arch()->preset()->php();

Bu preset'ler Laravel best practice'lerini, güvenlik standartlarını ve PHP strict mode kurallarını otomatik olarak kontrol etmektedir.

Mutation Testing: Gerçek Test Kalitesini Değerlendirme

Code coverage yüksek olsa bile testlerin gerçekten etkili olup olmadığını mutation testing ile ölçmek mümkündür. Pest, Laravel ile entegre mutation testing desteği sunmaktadır.

bash
# Run mutation testing on a specific class
php artisan test --mutate --class=App\\Services\\PriceCalculator

Mutation testing, kaynak kodda küçük değişiklikler (mutasyonlar) yaparak testlerin bu değişiklikleri yakalayıp yakalamadığını kontrol etmektedir. Yakalanmayan mutasyonlar, testlerin yetersiz olduğunu göstermektedir.

php
// Example: this test has high coverage but low mutation score
it('calculates shipping cost', function () {
    $cost = calculateShipping(weight: 5.0, zone: 'domestic');

    // Only checks that the result is numeric
    expect($cost)->toBeFloat();
});

// Improved: catches mutations by asserting the exact value
it('calculates domestic shipping for 5kg package', function () {
    $cost = calculateShipping(weight: 5.0, zone: 'domestic');

    // Exact assertion catches operator and value mutations
    expect($cost)->toBe(12.50);
});

İlk test sadece sonucun float olduğunu kontrol etmekte, bu nedenle hesaplamadaki hatalar fark edilememektedir. İkinci test ise kesin değeri kontrol ederek, operatör veya değer değişikliklerini yakalamaktadır. Mutation score, test suite'inin gerçek etkinliğini gösteren önemli bir metriktir.

Mülakatlar İçin Kapsamlı Test Senaryoları

Teknik mülakatlarda sıklıkla karşılaşılan senaryo, bir CRUD işleminin testini yazmaktır. Aşağıda makale yayınlama işleminin testi yer almaktadır:

tests/Feature/ArticlePublishingTest.phpphp
use App\Models\Article;
use App\Models\User;

it('publishes a draft article and updates the timestamp', function () {
    $author = User::factory()->create(['role' => 'editor']);

    $article = Article::factory()
        ->for($author, 'author')
        ->draft()
        ->create(['title' => 'Testing Best Practices']);

    $this->actingAs($author)
        ->patchJson("/api/articles/{$article->id}/publish")
        ->assertOk()
        ->assertJsonPath('data.status', 'published');

    $article->refresh();

    expect($article->status)->toBe('published')
        ->and($article->published_at)->not->toBeNull()
        ->and($article->published_at->isToday())->toBeTrue();
});

Bu test, factory state'leri, ilişkili model oluşturma, HTTP assertion'ları ve chained expectation'ları bir arada kullanmaktadır. refresh() metodu veritabanından güncel veriyi çekmekte, assertJsonPath() ise nested JSON değerlerini kontrol etmektedir.

API authentication testleri de mülakatlarda sıkça sorulan konular arasındadır:

tests/Feature/ApiAuthenticationTest.phpphp
use App\Models\User;

describe('API Authentication', function () {
    it('rejects unauthenticated requests with 401', function () {
        $this->getJson('/api/profile')
            ->assertUnauthorized();
    });

    it('returns the authenticated user profile', function () {
        $user = User::factory()->create([
            'name' => 'John Doe',
            'email' => 'john@example.com',
        ]);

        $this->actingAs($user)
            ->getJson('/api/profile')
            ->assertOk()
            ->assertJson([
                'data' => [
                    'name' => 'John Doe',
                    'email' => 'john@example.com',
                ],
            ]);
    });

    it('validates required fields on registration', function () {
        $this->postJson('/api/register', [])
            ->assertUnprocessable()
            ->assertJsonValidationErrors(['name', 'email', 'password']);
    });
});

Bu test grubu authentication middleware'inin çalışmasını, başarılı login sonrası kullanıcı verilerinin döndürülmesini ve validation hatalarının doğru formatlanmasını kontrol etmektedir.

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Teknik Mülakat Soruları: Laravel Testing

Laravel testing konusunda teknik mülakatlarda karşılaşılabilecek sorular ve beklenen cevap yaklaşımları aşağıda sıralanmaktadır:

RefreshDatabase ve DatabaseTransactions arasındaki fark nedir?

RefreshDatabase her test öncesinde migration'ları çalıştırır ve veritabanını sıfırlar. DatabaseTransactions ise her testi bir transaction içinde çalıştırır ve test sonunda rollback yapar. RefreshDatabase daha yavaş ancak daha güvenilirdir; DatabaseTransactions ise seeded data ile çalışırken tercih edilebilir.

Pest'te higher-order test nedir?

Higher-order testler, test metodlarını doğrudan chain'leyerek yazmayı sağlar. Örneğin it('has dashboard')->get('/dashboard')->assertOk() şeklinde tek satırda test yazılabilir. Bu yaklaşım basit assertion'lar için kodu önemli ölçüde kısaltmaktadır.

Facade mocking ve dependency injection mock'lama arasındaki fark nedir?

Facade mocking Laravel'in built-in fake metodlarını kullanır ve gerçek implementasyonu tamamen bypass eder. DI mock'lama ise Mockery ile belirli metodların davranışını tanımlar ve container'a bind eder. Facade mocking daha basitken, DI mock'lama daha granüler kontrol sağlamaktadır.

Architecture testleri hangi durumlarda kullanılmalıdır?

Architecture testleri, ekip genelinde tutarlılığı sağlamak, best practice'leri zorlamak ve teknik borç birikimini önlemek için kullanılmalıdır. Özellikle büyük ekiplerde ve uzun ömürlü projelerde kritik öneme sahiptir.

Test coverage ile mutation score arasındaki ilişki nedir?

Test coverage kodun ne kadarının çalıştırıldığını gösterirken, mutation score testlerin ne kadar etkili olduğunu gösterir. Yüksek coverage düşük mutation score ile birlikte gelebilir; bu durum testlerin assertion kalitesinin düşük olduğunu göstermektedir.

Sonuç

Laravel uygulamalarında profesyonel düzeyde test yazmak, Pest framework'ü ile oldukça verimli hale gelmektedir. Feature testlerden unit testlere, facade mocking'den mimari testlere kadar Laravel'in sunduğu test altyapısı kapsamlı bir coverage sağlamaktadır. Mutation testing ile test kalitesi ölçülebilmekte, architecture testleri ile proje standartları zorlanabilmektedir.

Teknik mülakatlarda Laravel testing konusunda başarılı olmak için yalnızca syntax bilgisi yeterli değildir. Test stratejileri, mocking yaklaşımları, performans optimizasyonları ve best practice'ler hakkında derinlemesine bilgi sahibi olmak gerekmektedir. Bu makalede sunulan örnekler ve açıklamalar, hem günlük geliştirme pratiğinde hem de mülakat hazırlığında referans olarak kullanılabilecek niteliktedir.

Etiketler

#laravel
#testing
#pest
#php
#interview

Paylaş

İlgili makaleler