froquiz-logoFroquiz
  • Anasayfa
  • Quizler
  • Blog
  • Hakkımızda
Froquiz

Yazılım mühendisleri için en kapsamlı quiz platformu. 5000+ soru ile kendinizi test edin ve kariyerinizi geliştirin.

LinkedIn

Platform

  • Quizlere Başla
  • Konular
  • Blog
  • Profilim
  • Giriş Yap

Hakkında

  • Biz Kimiz?
  • İletişim

Yasal

  • Gizlilik Politikası
  • Kullanım Koşulları

© 2026 Froquiz. Tüm hakları saklıdır.Teknoloji tutkuyla yapıldı
Blog/Test Yazmanın Doğru Yolu: Unit, Integration ve E2E Testler

Test Yazmanın Doğru Yolu: Unit, Integration ve E2E Testler

"Test yaz" demek kolay. Neyi test etmeli, nasıl yazmalı, ne kadar yeterli? Test piramidinden TDD'ye, mock kullanımından coverage'a kadar gerçekten işe yarayan test stratejisini öğrenin.

Yusuf SeyitoğluYusuf Seyitoğlu|
6 Mart 20266 Mar
|
36 görüntülenme
|
10 dk okuma

Test Yazmanın Doğru Yolu: Unit, Integration ve E2E Testler

"Bu kod test edilemez" cümlesini duydunuz mu? Ya da daha kötüsü, kendiniz söylediniz mi? Bu cümle genellikle kodun değil, kodun nasıl tasarlandığının bir sorunudur.

Test yazmak bir angarya değil, tasarım aracıdır. Zor test edilen kod genellikle kötü tasarlanmış koddur. İyi test yazmak öğrenilebilir — ama önce neyi neden test ettiğinizi anlamak gerekiyor.

Test Piramidi: Denge Nerede?

Test stratejisinin en güçlü görsel metaforu test piramididir.

           /\
          /  \
         / E2E\        Az sayıda, yavaş, pahalı
        /------\
       /        \
      /Integration\    Orta sayıda, orta hız
     /------------\
    /              \
   /   Unit Tests   \  Çok sayıda, hızlı, ucuz
  /------------------\

Unit testler (%70): Tek bir fonksiyon veya sınıfı test eder. Bağımlılıklar mock'lanır. Milisaniyeler içinde çalışır.

Integration testler (%20): Birden fazla bileşenin birlikte çalışmasını test eder. Gerçek veritabanı, gerçek servisler.

E2E testler (%10): Uygulamayı gerçek kullanıcı gibi test eder. Tarayıcı üzerinden akışları doğrular.

Unit Test: Hızlı ve İzole

Unit test, bir kod birimini tüm bağımlılıklarından izole ederek test eder. Bağımlılıklar mock ile değiştirilir.

import { OrderService } from "./OrderService"; describe("OrderService", () => { let orderService: OrderService; let mockOrderRepo: jest.Mocked<OrderRepository>; let mockEmailService: jest.Mocked<EmailService>; let mockInventoryService: jest.Mocked<InventoryService>; beforeEach(() => { mockOrderRepo = { create: jest.fn(), findById: jest.fn() } as any; mockEmailService = { sendConfirmation: jest.fn().mockResolvedValue(undefined) } as any; mockInventoryService = { checkStock: jest.fn() } as any; orderService = new OrderService( mockOrderRepo, mockEmailService, mockInventoryService ); }); it("should create order when stock is sufficient", async () => { // Arrange const userId = "user-123"; const items = [{ productId: "prod-1", quantity: 2, price: 50 }]; const expectedOrder = { id: "ord-1", userId, items, total: 100 }; mockInventoryService.checkStock.mockResolvedValue(10); mockOrderRepo.create.mockResolvedValue(expectedOrder); // Act const result = await orderService.createOrder(userId, items); // Assert expect(result).toEqual(expectedOrder); expect(mockOrderRepo.create).toHaveBeenCalledWith({ userId, items, total: 100 }); expect(mockEmailService.sendConfirmation).toHaveBeenCalledWith( userId, expectedOrder ); }); it("should throw error when stock is insufficient", async () => { mockInventoryService.checkStock.mockResolvedValue(1); const items = [{ productId: "prod-1", quantity: 5, price: 50 }]; await expect( orderService.createOrder("user-123", items) ).rejects.toThrow("Insufficient stock for product prod-1"); expect(mockOrderRepo.create).not.toHaveBeenCalled(); }); });

AAA Pattern: Arrange (hazırla) → Act (çalıştır) → Assert (doğrula). Her test bu üç aşamayı net biçimde içermeli.

Integration Test: Gerçek Bağlantılarla

Integration testler, bileşenlerin birlikte çalışmasını test eder. Gerçek veritabanı, gerçek HTTP katmanı kullanılır.

import request from "supertest"; import { app } from "../app"; import { db } from "../database"; describe("POST /api/orders", () => { beforeEach(async () => { await db.migrate.latest(); await db.seed.run(); }); afterEach(async () => { await db.migrate.rollback(); }); it("should create order and return 201", async () => { const response = await request(app) .post("/api/orders") .set("Authorization", "Bearer valid-test-token") .send({ items: [{ productId: "prod-1", quantity: 2 }] }) .expect(201); expect(response.body).toMatchObject({ id: expect.any(String), status: "pending", total: expect.any(Number) }); // Veritabanında gerçekten oluşturuldu mu? const order = await db("orders").where({ id: response.body.id }).first(); expect(order).toBeDefined(); expect(order.status).toBe("pending"); }); it("should return 400 when items are missing", async () => { const response = await request(app) .post("/api/orders") .set("Authorization", "Bearer valid-test-token") .send({}) .expect(400); expect(response.body.error).toContain("items"); }); });

E2E Test: Kullanıcı Gibi Test Etmek

import { test, expect } from "@playwright/test"; test.describe("Checkout Flow", () => { test.beforeEach(async ({ page }) => { await page.goto("/login"); await page.fill("input[name=email]", "test@example.com"); await page.fill("input[name=password]", "password123"); await page.click("button[type=submit]"); await expect(page).toHaveURL("/dashboard"); }); test("user can complete purchase", async ({ page }) => { await page.goto("/products/laptop-pro"); await page.click("button[data-testid=add-to-cart]"); await expect(page.locator("[data-testid=cart-count]")).toHaveText("1"); await page.goto("/cart"); await page.click("button[data-testid=checkout]"); await page.fill("input[name=card-number]", "4242424242424242"); await page.fill("input[name=expiry]", "12/26"); await page.fill("input[name=cvv]", "123"); await page.click("button[data-testid=pay]"); await expect(page).toHaveURL(/\/orders\/\w+\/success/); await expect(page.locator("h1")).toContainText("Order Confirmed"); }); });

TDD: Test Önce Yaklaşımı

Test Driven Development (TDD), kodu yazmadan önce testi yazmayı önerir. Döngü üç adımdan oluşur:

Red: Başarısız test yaz. Kod henüz yok.

Green: Testi geçirecek minimum kodu yaz.

Refactor: Testi bozmadan kodu temizle.

// 1. RED: Fonksiyon henüz yok, test yazildi it("should convert USD to EUR", () => { const result = convertCurrency(100, "USD", "EUR", 0.92); expect(result).toBe(92); }); // 2. GREEN: Minimum implementasyon function convertCurrency(amount: number, from: string, to: string, rate: number) { return amount * rate; } // 3. REFACTOR: Temizle, test hala geciyor mu? function convertCurrency( amount: number, fromCurrency: string, toCurrency: string, exchangeRate: number ): number { if (amount < 0) throw new Error("Amount cannot be negative"); return Math.round(amount * exchangeRate * 100) / 100; }

Test Coverage: Yanıltıcı Metrik

%100 coverage hedeflemek çoğu zaman yanlış odaklanmadır. Coverage "kod çalıştırıldı mı?" ölçer, "doğru çalışıyor mu?" değil.

// %100 coverage ama hicbir sey test etmiyor function add(a: number, b: number) { return a + b; } test("add function", () => { add(1, 2); // Cagirildi ama sonuc kontrol edilmedi expect(true).toBe(true); });

Coverage bir güvenlik ağı değil, bir sinyal. Düşük coverage "burada test eksik" der. Yüksek coverage "burası güvende" demez.

Odaklanmanız gereken: kritik iş mantığı tam test edilmiş mi, edge case'ler kapsanmış mı, testler gerçekten bir şey doğruluyor mu?

Test yazmak başlangıçta yavaşlatır. Ama bakım sürecinde, refactor yaparken, yeni özellik eklerken — o yatırımın karşılığını alırsınız. Testler olmadan büyüyen bir kod tabanı, üzerine inşaat yapılan bataklığa döner.

Yazar Hakkında

Yusuf Seyitoğlu

Yusuf Seyitoğlu

View Profile →

Diğer Yazılar

  • CI/CD Pipeline Nedir? Kod Yazmaktan Production'a Giden Otomatik Yol

    6 Mar

  • TypeScript Nedir? JavaScript Geliştiricisinin Tip Sistemine Gerçek Giriş

    6 Mar

  • Kubernetes Nedir? Container Orkestrasyon'u Sıfırdan Anlamak

    6 Mar

← Tüm Bloglar