Test Temelleri
Neden Test Yazarız?
Bir köprü inşa ettiğini düşün. Köprü bitti, güzel görünüyor. Peki üzerinden kamyon geçtiğinde çökmeyeceğinden emin misin? "Bence sağlam" demek yeterli mi? Tabii ki hayır — mühendisler köprüyü test eder. Yük testleri, rüzgar testleri, deprem simülasyonları yapar. Çünkü "bence çalışıyor" ile "test ettim, çalışıyor" arasında hayat kurtaran bir fark var.
Yazılımda da durum aynı. Kodu yazarsın, tarayıcıda açarsın, birkaç butona tıklarsın, "çalışıyor" dersin. Ama edge case'leri test ettin mi? Kullanıcı boş form gönderirse? Sunucu hata dönerse? İnternet kesilirse? 3 ay sonra bir arkadaşın kodu değiştirdiğinde hâlâ çalışıyor mu?
Test yazmak, kodunun doğru çalıştığının kanıtıdır. "Bence çalışıyor" yerine "ispatladım, çalışıyor" dersin.
Test Yazmanın Gerçek Faydaları
Test yazmak ilk bakışta "ekstra iş" gibi görünür. Ama aslında toplam iş miktarını azaltır:
Bug'ları erken yakalar — Production'da bug bulmak, geliştirme sırasında bulmaktan 100x daha pahalı
Refactoring cesareti verir — "Kodu değiştirdim, hiçbir test kırılmadı" = güven
Canlı dokümantasyon — Testler, kodun nasıl kullanılacağını gösterir
Tasarımı iyileştirir — Test yazması zor kod = kötü tasarım sinyali
Regression önler — Eski bug'lar geri dönmez
Test yazmayan geliştirici:
Kod yaz → Manuel test → "Çalışıyor" → Deploy → 🐛 Bug! → Panic fix → 🐛 Yeni bug
Test yazan geliştirici:
Kod yaz → Test yaz → Otomatik test → Güvenle deploy → ✅ Çalışıyor → 😌 Huzur💡 İpucu: "Test yazmaya vaktim yok" aslında "Bug düzeltmeye vaktim var" demektir. Tecrübeli geliştiriciler bilir: test yazmak zamanı tasarruf eder, harcamaz.
Test Yazmanın Maliyeti vs Getirisi
Proje Büyüklüğü Test Yok Test Var
──────────────────────────────────────────────
Küçük proje Hızlı ✅ Biraz yavaş ⚠️
(< 1000 satır) Manuel test OK Overkill olabilir
Orta proje Riskli ⚠️ Güvenli ✅
(1000-10000) Regression başlar Refactor cesareti
Büyük proje Tehlikeli ❌ Zorunlu ✅
(> 10000) Dokunmaya kork! Değişim güvenli
Ekip projesi Kabus ❌ Olmazsa olmaz ✅
(2+ geliştirici) "Kim bozdu?" CI/CD güvenTest Türleri: Piramit Modeli
Tüm testler aynı değildir. Farklı seviyelerde, farklı şeyleri test eden üç ana tür var:
╱╲
╱ ╲ E2E Tests (az, yavaş, pahalı)
╱ E2E╲ Gerçek tarayıcı, gerçek API
╱──────╲
╱ ╲ Integration Tests (orta)
╱Integration╲ Bileşenler arası etkileşim
╱──────────────╲
╱ ╲ Unit Tests (çok, hızlı, ucuz)
╱ Unit Tests ╲ Tek fonksiyon/modül
╱────────────────────╲Bu Test Piramidi — Martin Fowler tarafından popülerleştirildi. Alt kısım geniş (çok test), üst kısım dar (az test).
Unit Test: Tek Parçayı Test Et
Bir fonksiyon, bir class, bir modül — izole bir parçanın doğru çalıştığını kontrol eder. Dış bağımlılıklar (veritabanı, API, dosya sistemi) mock edilir.
Analoji: Bir arabanın motor kontrolü gibi. Motoru araçtan çıkarıp, tek başına test edersin. Benzin veriyor musun? Ateşleme çalışıyor mu? Piston hareket ediyor mu? Direksiyon, tekerlek, fren — hepsi ayrı test edilir.
// Fonksiyon
function calculateDiscount(price, discountPercent) {
if (price < 0) throw new Error("Fiyat negatif olamaz");
if (discountPercent < 0 || discountPercent > 100) {
throw new Error("İndirim yüzdesi 0-100 arasında olmalı");
}
return price - (price * discountPercent / 100);
}
// Unit Test
test("calculateDiscount: %20 indirim", () => {
expect(calculateDiscount(100, 20)).toBe(80);
});
test("calculateDiscount: negatif fiyat hata fırlatır", () => {
expect(() => calculateDiscount(-10, 20)).toThrow("Fiyat negatif olamaz");
});
test("calculateDiscount: %0 indirim fiyatı değiştirmez", () => {
expect(calculateDiscount(50, 0)).toBe(50);
});
test("calculateDiscount: %100 indirim bedava yapar", () => {
expect(calculateDiscount(50, 100)).toBe(0);
});
test("calculateDiscount: geçersiz indirim yüzdesi hata fırlatır", () => {
expect(() => calculateDiscount(100, 150)).toThrow("İndirim yüzdesi 0-100 arasında olmalı");
expect(() => calculateDiscount(100, -10)).toThrow("İndirim yüzdesi 0-100 arasında olmalı");
});Özellikleri:
⚡ Çok hızlı (milisaniye)
🔧 Kolay yazılır ve debug edilir
🎯 Hatanın tam yerini gösterir
📦 İzole — dış bağımlılık yok
Integration Test: Parçaların Birlikte Çalışması
Birden fazla modülün birlikte doğru çalıştığını kontrol eder. "Her parça tek başına çalışıyor ama birlikte de çalışıyor mu?"
Analoji: Motor, vites kutusu ve tekerleği birlikte test et. Motor çalışıyor, vites de çalışıyor — ama birbirine bağladığında güç aktarımı doğru mu? Vites 3'e geçtiğinde teker doğru hızda dönüyor mu?
// UserService + Database + EmailService birlikte
test("kullanıcı oluşturma: veritabanına kaydeder ve email gönderir", async () => {
const db = new TestDatabase(); // Gerçek veritabanı (test DB)
const emailService = new EmailService();
const userService = new UserService(db, emailService);
const user = await userService.createUser({
name: "Ali",
email: "ali@test.com",
});
// Veritabanında var mı?
const saved = await db.findById(user.id);
expect(saved).not.toBeNull();
expect(saved.name).toBe("Ali");
// Email gönderildi mi?
expect(emailService.sentEmails).toHaveLength(1);
expect(emailService.sentEmails[0].to).toBe("ali@test.com");
});Özellikleri:
🔗 Modüller arası etkileşimi test eder
🕐 Unit'e göre yavaş (veritabanı, dosya sistemi)
🧩 Gerçek bağımlılıklar kullanılabilir (test DB, test server)
🐛 "Integration bug'larını" yakalar — "her parça çalışıyor ama birlikte bozuluyor"
E2E (End-to-End) Test: Gerçek Kullanıcı Senaryosu
Uygulamayı gerçek bir kullanıcı gibi test eder. Gerçek tarayıcı açılır, butonlara tıklanır, formlar doldurulur, sonuçlar kontrol edilir.
Analoji: Arabayı yolda sürmek. Motor, vites, direksiyon, fren — her şey birlikte, gerçek koşullarda. Yokuşta kalkış, viraj, acil fren. Gerçek kullanıcı deneyimini simüle edersin.
// Cypress veya Playwright ile
test("kullanıcı giriş yapabilir", async ({ page }) => {
// Gerçek tarayıcıda gerçek uygulama
await page.goto("https://myapp.com/login");
// Form doldur
await page.fill("#email", "ali@test.com");
await page.fill("#password", "güçlüşifre123");
await page.click("#login-button");
// Dashboard'a yönlendirildi mi?
await expect(page).toHaveURL("/dashboard");
await expect(page.locator("h1")).toHaveText("Hoş Geldiniz, Ali");
});
test("yanlış şifre ile giriş yapılamaz", async ({ page }) => {
await page.goto("https://myapp.com/login");
await page.fill("#email", "ali@test.com");
await page.fill("#password", "yanlisşifre");
await page.click("#login-button");
// Hata mesajı gösteriliyor mu?
await expect(page.locator(".error-message")).toHaveText("Geçersiz email veya şifre");
await expect(page).toHaveURL("/login"); // Hâlâ login sayfasında
});Özellikleri:
🌐 Gerçek tarayıcı, gerçek sunucu
🐢 En yavaş test türü (saniye-dakika)
💰 En pahalı (kurulum, bakım, CI süresi)
✅ En yüksek güven seviyesi — "Gerçekten çalışıyor"
🔧 Bakımı zor — UI değiştiğinde kırılabilir
Hangi Türden Ne Kadar?
Piramit kuralı:
├── Unit Tests: ~70% (yüzlerce/binlerce)
├── Integration Tests: ~20% (onlarca)
└── E2E Tests: ~10% (kritik akışlar)Neden bu oran?
Unit testler hızlı ve ucuz — çok yaz
Integration testler orta — önemli etkileşimleri test et
E2E testler yavaş ve kırılgan — sadece kritik kullanıcı akışlarını test et
E2E testi yazılması gereken kritik akışlar:
├── Kullanıcı kaydı ve giriş
├── Ödeme akışı (sepet → ödeme → onay)
├── Temel navigasyon (sayfa geçişleri)
└── Veri kaybına yol açabilecek akışlar⚠️ Dikkat: "Ice cream cone" anti-pattern'ından kaçın — çok E2E, az unit. Bu durumda test suite'in yavaş, kırılgan ve bakımı zor olur. Piramidin tabanını güçlü tut.
TDD vs BDD
TDD: Test-Driven Development
TDD'nin mantığı şu: önce test yaz, sonra kodu yaz. Kulağa garip gelebilir — henüz kod yok, neyi test edeceksin? Ama bu yaklaşım seni düşünmeye zorlar: "Bu fonksiyon ne yapmalı? Hangi girdilere hangi çıktıyı vermeli?"
TDD Döngüsü (Red-Green-Refactor):
1. 🔴 RED → Test yaz → Test başarısız olur (kod yok çünkü)
2. 🟢 GREEN → Testi geçecek minimum kodu yaz
3. 🔵 REFACTOR → Kodu temizle, test hâlâ geçiyor mu kontrol et
4. → 1'e dönBunu pratikte görelim:
// 1. 🔴 RED — Önce test yaz (fonksiyon henüz YOK)
test("palindrome kontrolü: 'kayak' palindrome olmalı", () => {
expect(isPalindrome("kayak")).toBe(true);
});
test("palindrome kontrolü: 'merhaba' palindrome olmamalı", () => {
expect(isPalindrome("merhaba")).toBe(false);
});
test("palindrome kontrolü: boş string palindrome olmalı", () => {
expect(isPalindrome("")).toBe(true);
});
test("palindrome kontrolü: büyük-küçük harf duyarsız", () => {
expect(isPalindrome("Kayak")).toBe(true);
});
// Testleri çalıştır → HEPSİ KIRMIZI ❌ (isPalindrome tanımlı değil)
// 2. 🟢 GREEN — Testi geçecek EN BASİT kodu yaz
function isPalindrome(str) {
const normalized = str.toLowerCase();
return normalized === normalized.split("").reverse().join("");
}
// Testleri çalıştır → HEPSİ YEŞİL ✅
// 3. 🔵 REFACTOR — Kodu iyileştir (testler hâlâ geçmeli)
function isPalindrome(str) {
const s = str.toLowerCase();
let left = 0, right = s.length - 1;
while (left < right) {
if (s[left] !== s[right]) return false;
left++;
right--;
}
return true;
}
// Testleri çalıştır → HEPSİ YEŞİL ✅ (refactor başarılı)TDD'nin faydaları:
Testler her zaman yazılır (sonraya kalmaz)
Daha iyi API tasarımı — "kullanıcı perspektifinden" düşünürsün
Minimal kod — sadece gerekli olanı yazarsın (YAGNI prensibi)
Güvenli refactoring — her değişiklik anında test edilir
TDD ne zaman kullanmalı:
Yeni bir fonksiyon/modül yazarken
Bir bug fix yaparken (önce bug'ı reproduce eden test yaz)
Algoritma/hesaplama kodu yazarken
TDD ne zaman zor/gereksiz:
UI prototipleme — hızlı deney yaparken
Bir kerelik scriptler
API tasarımı henüz net değilken
BDD: Behavior-Driven Development
BDD, TDD'nin bir uzantısı — teknik olmayan kişilerin de anlayabileceği bir dille test yazarsın. "Given-When-Then" formatını kullanır:
// BDD stili — describe/it ile okunabilir yapı
describe("Alışveriş Sepeti", () => {
describe("ürün eklendiğinde", () => {
it("sepetteki ürün sayısı artmalı", () => {
// Given: Boş sepet
const cart = new ShoppingCart();
// When: Ürün eklenir
cart.addItem({ id: 1, name: "Laptop", price: 5000 });
// Then: Sayı 1 olur
expect(cart.itemCount).toBe(1);
});
it("toplam fiyat güncellenmeli", () => {
const cart = new ShoppingCart();
cart.addItem({ id: 1, name: "Laptop", price: 5000 });
cart.addItem({ id: 2, name: "Mouse", price: 200 });
expect(cart.totalPrice).toBe(5200);
});
});
describe("ürün silindiğinde", () => {
it("sepetten kalkmalı", () => {
const cart = new ShoppingCart();
cart.addItem({ id: 1, name: "Laptop", price: 5000 });
cart.removeItem(1);
expect(cart.itemCount).toBe(0);
});
it("boş sepetten silme hata vermemeli", () => {
const cart = new ShoppingCart();
expect(() => cart.removeItem(999)).not.toThrow();
});
});
});TDD vs BDD:
| Özellik | TDD | BDD |
|---|---|---|
| Odak | Fonksiyon/modül doğruluğu | Kullanıcı davranışı |
| Dil | Teknik | İş dili (business language) |
| Format | test/expect | describe/it (Given-When-Then) |
| Kimin için | Geliştiriciler | Geliştirici + Ürün ekibi |
| Granülarity | Birim seviyesi | Özellik/senaryo seviyesi |
Pratikte çoğu ekip ikisini birlikte kullanır: BDD formatında yazılmış, TDD döngüsüyle geliştirilmiş testler.
Test Araçları Ekosistemi
JavaScript/TypeScript dünyasında birçok test aracı var. Hangisini ne zaman kullanacağını bilmek önemli:
Unit / Integration Test Araçları
| Araç | Açıklama | Kullanım |
|---|---|---|
| Jest | Meta'nın (Facebook) test framework'ü | React projeleri, genel amaçlı |
| Vitest | Vite tabanlı, Jest uyumlu, hızlı | Vite projeler, modern ✅ |
| Mocha + Chai | Esnek, modüler yapı | Eski projeler, Node.js |
| Node Test Runner | Node.js yerleşik (v18+) | Harici bağımlılık istemiyorsan |
// Jest vs Vitest — neredeyse aynı API
// Jest
const { describe, test, expect } = require("@jest/globals");
// Vitest — Vite projelerinde önerilir
import { describe, test, expect } from "vitest";
// İkisinde de aynı şekilde test yazarsın:
test("1 + 1 = 2", () => {
expect(1 + 1).toBe(2);
});E2E Test Araçları
| Araç | Açıklama | Kullanım |
|---|---|---|
| Playwright | Microsoft, cross-browser | Modern projeler, CI/CD ✅ |
| Cypress | Developer-friendly | Frontend odaklı projeler |
| Puppeteer | Google, Chrome odaklı | Chrome otomasyon, scraping |
// Playwright — cross-browser (Chromium, Firefox, WebKit)
import { test, expect } from "@playwright/test";
test("ana sayfa başlığı doğru", async ({ page }) => {
await page.goto("https://myapp.com");
await expect(page).toHaveTitle(/My App/);
});
// Cypress — component testing de destekler
describe("Login", () => {
it("should login successfully", () => {
cy.visit("/login");
cy.get("#email").type("ali@test.com");
cy.get("#password").type("password123");
cy.get("button[type=submit]").click();
cy.url().should("include", "/dashboard");
});
});Assertion Kütüphaneleri
// Jest/Vitest — yerleşik expect
expect(value).toBe(expected); // Strict equality (===)
expect(value).toEqual(expected); // Deep equality
expect(value).toBeTruthy(); // Truthy check
expect(value).toBeNull(); // null check
expect(value).toBeDefined(); // !== undefined
expect(value).toContain(item); // Array/String contains
expect(fn).toThrow(); // Hata fırlatma
expect(fn).toThrow("mesaj"); // Belirli hata mesajı
expect(value).toBeGreaterThan(3); // > 3
expect(value).toBeCloseTo(0.3, 5); // Float karşılaştırma
expect(value).toMatch(/regex/); // RegExp
expect(value).toHaveProperty("name"); // Nesne property
// Negation — .not ile
expect(value).not.toBe(other);
expect(fn).not.toThrow();İlk Test: Adım Adım
Test yazmayı hiç bilmeyen biri için sıfırdan:
# 1. Proje oluştur
mkdir my-project && cd my-project
npm init -y
# 2. Jest yükle
npm install -D jest
# 3. Jest yapılandır (package.json){
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
}
}// src/math.js — Test edilecek kod
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
function divide(a, b) {
if (b === 0) throw new Error("Sıfıra bölünemez");
return a / b;
}
module.exports = { add, multiply, divide };// src/math.test.js — Test dosyası (.test.js uzantısı)
const { add, multiply, divide } = require("./math");
describe("Math modülü", () => {
describe("add", () => {
test("iki pozitif sayıyı toplar", () => {
expect(add(2, 3)).toBe(5);
});
test("negatif sayılarla çalışır", () => {
expect(add(-1, -2)).toBe(-3);
});
test("sıfır ile toplamak değeri değiştirmez", () => {
expect(add(5, 0)).toBe(5);
});
});
describe("multiply", () => {
test("iki sayıyı çarpar", () => {
expect(multiply(3, 4)).toBe(12);
});
test("sıfır ile çarpmak sıfır verir", () => {
expect(multiply(5, 0)).toBe(0);
});
test("negatif sayı ile çarpmak sonucu negatif yapar", () => {
expect(multiply(3, -2)).toBe(-6);
});
});
describe("divide", () => {
test("iki sayıyı böler", () => {
expect(divide(10, 2)).toBe(5);
});
test("sıfıra bölme hata fırlatır", () => {
expect(() => divide(10, 0)).toThrow("Sıfıra bölünemez");
});
test("ondalıklı sonuç döndürebilir", () => {
expect(divide(7, 2)).toBeCloseTo(3.5);
});
});
});# 4. Testleri çalıştır
npm test
# Çıktı:
# PASS src/math.test.js
# Math modülü
# add
# ✓ iki pozitif sayıyı toplar (2ms)
# ✓ negatif sayılarla çalışır
# ✓ sıfır ile toplamak değeri değiştirmez
# multiply
# ✓ iki sayıyı çarpar (1ms)
# ✓ sıfır ile çarpmak sıfır verir
# ✓ negatif sayı ile çarpmak sonucu negatif yapar
# divide
# ✓ iki sayıyı böler (1ms)
# ✓ sıfıra bölme hata fırlatır
# ✓ ondalıklı sonuç döndürebilir
#
# Test Suites: 1 passed, 1 total
# Tests: 9 passed, 9 total
# Time: 0.5sTest İsimlendirme
İyi test ismi, testin ne kontrol ettiğini okumadan anlatır:
// ❌ Kötü isimler — ne test edildiği belirsiz
test("test1", () => { /* ... */ });
test("çalışıyor", () => { /* ... */ });
test("add fonksiyonu", () => { /* ... */ });
test("hata testi", () => { /* ... */ });
// ✅ İyi isimler — ne yaptığını ve ne beklediğini söyler
test("add: iki pozitif sayıyı topladığında doğru sonuç döndürür", () => {});
test("login: yanlış şifre ile giriş yapıldığında hata fırlatır", () => {});
test("sepet: aynı ürün tekrar eklendiğinde miktarı artırır", () => {});
test("formatDate: null tarih verildiğinde 'N/A' döndürür", () => {});
// Pattern: [birim]: [senaryo] → [beklenen sonuç]
test("calculateTax: %18 KDV uygulandığında fiyatın %18'ini ekler", () => {
expect(calculateTax(100, 18)).toBe(118);
});💡 İpucu: Testi oku — eğer test isminden ne test edildiğini anlayamıyorsan, ismini değiştir. Test isimleri aynı zamanda dokümantasyon işlevi görür.
jest --verboseile test isimleri listesine bak — bir specification gibi okunmalı.
Neyi Test Etmeli, Neyi Etmemeli?
Test ET ✅
İş mantığı: Hesaplamalar, validasyonlar, dönüşümler
Edge case'ler: Boş input, sınır değerler, null/undefined
Hata senaryoları: Hatalı input'ta doğru hata mesajı dönüyor mu?
Entegrasyon noktaları: API çağrıları, veritabanı sorguları (mock ile)
Kritik kullanıcı akışları: Giriş, kayıt, ödeme
Regresyon: Düzeltilen bug'lar için test ekle (tekrar oluşmasın)
Test ETME ❌
Framework/kütüphane kodu: React'ın
useState'ini test etme — React ekibi zaten ettiTrivial getter/setter:
getName() { return this.name; }— test etmeye değmezImplementasyon detayları: Kodun "nasıl" yaptığını değil, "ne" yaptığını test et
Dış servisler: Gerçek API'leri unit testte test etme — mock kullan
Sabit değerler:
const PI = 3.14159— test gereksiz
// ❌ İmplementasyon detayını test etme
test("sort fonksiyonu quicksort algoritması kullanır", () => {
// Bu kötü — algoritma değişse test kırılır ama sonuç aynı kalır
});
// ✅ Davranışı test et
test("sort fonksiyonu diziyi küçükten büyüğe sıralar", () => {
expect(sort([3, 1, 2])).toEqual([1, 2, 3]);
});
// ❌ Trivial — test etmeye değmez
test("user.getName() ismi döndürür", () => {
const user = new User("Ali");
expect(user.getName()).toBe("Ali"); // Getter'ı test etmenin anlamı yok
});
// ✅ Anlamlı — iş mantığı test ediliyor
test("user.getDisplayName() isim ve soyismi birleştirir", () => {
const user = new User("Ali", "Yılmaz");
expect(user.getDisplayName()).toBe("Ali Yılmaz");
});Test Yaşam Döngüsü: Setup ve Teardown
describe("Database tests", () => {
let db;
// Tüm testlerden ÖNCE bir kez çalışır
beforeAll(async () => {
db = await Database.connect("test-db");
console.log("DB bağlantısı kuruldu");
});
// Her testten ÖNCE çalışır
beforeEach(async () => {
await db.clear(); // Her test temiz başlasın
});
// Her testten SONRA çalışır
afterEach(() => {
jest.restoreAllMocks(); // Mock'ları temizle
});
// Tüm testlerden SONRA bir kez çalışır
afterAll(async () => {
await db.disconnect();
console.log("DB bağlantısı kapatıldı");
});
test("kullanıcı oluşturur", async () => {
await db.createUser({ name: "Ali" });
const users = await db.getAllUsers();
expect(users).toHaveLength(1);
});
test("kullanıcı siler", async () => {
// beforeEach sayesinde DB temiz — önceki testten etkilenmez
await db.createUser({ name: "Ayşe" });
await db.deleteUser("Ayşe");
const users = await db.getAllUsers();
expect(users).toHaveLength(0);
});
});Çalışma sırası:
beforeAll → DB bağlan (1 kez)
beforeEach → DB temizle
test 1 → kullanıcı oluştur
afterEach → mock temizle
beforeEach → DB temizle
test 2 → kullanıcı sil
afterEach → mock temizle
afterAll → DB kapat (1 kez)Yaygın Hatalar
1. Asenkron Testi Yanlış Yazmak
// ❌ async test'te await unutmak — test her zaman geçer!
test("veri yüklenir", () => {
// Bu promise resolve olmadan test biter — assert çalışmaz
fetchData().then(data => {
expect(data).toHaveLength(5); // ASLA ÇALIŞMAZ ama test geçer!
});
});
// ✅ Doğru: async/await kullan
test("veri yüklenir", async () => {
const data = await fetchData();
expect(data).toHaveLength(5);
});
// ✅ Veya: return promise
test("veri yüklenir", () => {
return fetchData().then(data => {
expect(data).toHaveLength(5);
});
});2. Testler Arası State Paylaşımı
// ❌ Shared state — testler birbirini etkiler
const users = [];
test("kullanıcı ekler", () => {
users.push("Ali");
expect(users).toHaveLength(1);
});
test("kullanıcı listesi boş olmalı", () => {
expect(users).toHaveLength(0); // ❌ FAIL — "Ali" hâlâ var!
});
// ✅ Her test kendi state'ini oluştursun
test("kullanıcı ekler", () => {
const users = [];
users.push("Ali");
expect(users).toHaveLength(1);
});3. Assert Yazmmamak
// ❌ "Patlamazsa yeter" — hiçbir şeyi doğrulamıyor
test("processOrder çalışır", () => {
processOrder({ id: 1 }); // Hata fırlatmazsa "geçer"
// Assert YOK — çıktıyı kontrol etmiyorsun!
});
// ✅ Sonucu doğrula
test("processOrder sipariş ID döndürür", () => {
const result = processOrder({ id: 1 });
expect(result.orderId).toBeDefined();
expect(result.status).toBe("confirmed");
});Özet
Test yazmak "ekstra iş" değil, toplam iş miktarını azaltan bir yatırımdır. Bug'ları erken yakalar, refactoring cesareti verir, canlı dokümantasyon oluşturur.
Unit Test: Tek parçayı izole test eder. Hızlı, ucuz, çok yaz (~70%).
Integration Test: Parçaların birlikte çalışmasını test eder. Orta hız, orta miktar (~20%).
E2E Test: Gerçek kullanıcı senaryosu. Yavaş, pahalı, az yaz (~10%). Sadece kritik akışlar.
Test Piramidi: Altı geniş (unit), üstü dar (E2E). Anti-pattern: Ice cream cone (çok E2E, az unit).
TDD: Önce test yaz → kodu yaz → refactor et. Red-Green-Refactor döngüsü. İş mantığında çok faydalı.
BDD: İş dilinde test yaz (describe/it). Given-When-Then formatı. Ekip iletişimini güçlendirir.
Davranışı test et, implementasyon detayını değil. Test isimleri dokümantasyon gibi okunmalı.
AI Asistan
Sorularını yanıtlamaya hazır