Tüm Yazılar
KategoriAI
Okuma Süresi
14 dk okuma
Yayın Tarihi
...
Kelime Sayısı
3.392kelime

Kahveni hazırla - bu içerikli bir makale!

AI asistanları ile unit test üretme, edge case detection, coverage optimization, Claude Code + Cursor tandem, gerçek 50K LOC projede test yazma deneyimi.

AI Destekli Unit Test Üretimi: Claude Code + Cursor ile 2026 Workflow

# AI Destekli Unit Test Üretimi: Claude Code + Cursor ile 2026 Workflow

2025'te AI ile kod yazıyordunuz. 2026'da AI ile test yazıyorsunuz. Bu farkı küçümsemeyin — test yazma, çoğu zaman kod yazmaktan daha zor. Edge case'leri bulmak, mock stratejisi kurmak, assertion'ları doğru yazmak deneyim gerektiriyor. AI bu deneyimi herkes için erişilebilir kılıyor.

50.000 satır TypeScript koduna sahip bir projede AI yardımıyla test coverage'ı %78'den %94'e çıkardım. Bu süreçte öğrendiklerimi paylaşıyorum.

Pro Tip: AI'a test ürettirmeden önce "Bu fonksiyonun hangi edge case'leri olabilir?" diye sorun, doğrudan test yazdırmayın. Edge case listesi aldıktan sonra test üretimi çok daha hedefli oluyor ve gözden kaçan senaryolar azalıyor.

İçindekiler

  1. Neden AI Destekli Test?
  2. Claude Code ile Test Generation
  3. Cursor Test Agent Workflow
  4. Edge Case Mining Teknikleri
  5. Mocking Stratejileri
  6. Coverage Analizi ve Optimizasyon
  7. Property-Based Testing ile AI
  8. Snapshot Test Review
  9. Flaky Test Tespiti
  10. Team Workflow ve Standartlar

1. Neden AI Destekli Test?

Yazılım geliştiricilerin test yazmamasının iki ana nedeni var: zaman ve zihinsel efor. Bir fonksiyonu yazdıktan sonra test yazmak, aynı problemi ikinci kez çözmek gibi hissettiriyor. AI bu algıyı değiştiriyor.

Gerçek metrikler (50K LOC projede):

Metrik
AI Öncesi
AI Sonrası
Test yazma hızı
~15 test/gün
~60 test/gün (4x)
Test coverage
%78
%94
Edge case tespiti
Manual
AI assisted
Ortalama test kalitesi
Orta
Yüksek
Flaky test oranı
%12
%3

Bu rakamlar tek kişilik effort. Takım genelinde ölçüldüğünde etkisi daha da büyük — junior geliştiriciler deneyimli developerların seviyesinde test yazabiliyor.

AI'nın güçlü olduğu alanlar:

  • Boundary value analysis (sınır değer analizi)
  • Negative test cases (hata yolları)
  • Null/undefined handling
  • Type edge cases (0, empty string, very large numbers)
  • Async error scenarios
  • Race conditions (önerebilir, garantileyemez)

AI'nın zayıf olduğu alanlar:

  • Domain-specific business logic edge cases (müşteri sipariş kuralları, vergi hesaplama mantığı)
  • Integration test setup (gerçek database, external service)
  • Visual regression test
  • Performance benchmark test

2. Claude Code ile Test Generation

Claude Code'un /test komutu, açık dosyayı analiz edip test üretir. Ama raw output yerine prompt engineering ile çok daha kaliteli sonuç alınıyor.

Temel kullanım:

bash
1# Claude Code terminal'de
2claude
3 
4# Dosyayı açıkla ve test iste
5> /test src/lib/pricing.ts

Daha etkili prompt pattern'leri:

swift
1# Pattern 1: Edge case mining önce
2"Bu fonksiyonun edge case'lerini listele, henüz test yazma:
3[fonksiyon kodu]"
4 
5# Pattern 2: Belirli framework ile
6"Vitest kullanarak, AAA pattern ile (Arrange/Act/Assert)
7şu fonksiyon için test yaz. Her test independent olsun:
8[fonksiyon kodu]"
9 
10# Pattern 3: Coverage odaklı
11"Şu test suite'e bakarak, hangi dallar test edilmemiş?
12Eksik coverage için testler üret:
13[mevcut testler]
14[kaynak kod]"
15 
16# Pattern 4: Regression test
17"Bu bug fix için regression test yaz.
18Bug: [açıklama]
19Fix: [kod değişikliği]"

Gerçek örnek — pricing.ts:

typescript
1// src/lib/pricing.ts
2interface PricingInput {
3 basePrice: number;
4 quantity: number;
5 discountCode?: string;
6 userType: "guest" | "member" | "premium";
7}
8 
9interface PricingResult {
10 subtotal: number;
11 discount: number;
12 tax: number;
13 total: number;
14}
15 
16const DISCOUNT_CODES: Record = {
17 SAVE10: 0.1,
18 SAVE20: 0.2,
19 WELCOME: 0.15,
20};
21 
22const USER_TYPE_DISCOUNTS = {
23 guest: 0,
24 member: 0.05,
25 premium: 0.1,
26};
27 
28const TAX_RATE = 0.18;
29 
30export function calculatePrice(input: PricingInput): PricingResult {
31 if (input.basePrice < 0) {
32 throw new Error("Base price cannot be negative");
33 }
34 if (input.quantity <= 0) {
35 throw new Error("Quantity must be positive");
36 }
37 
38 const subtotal = input.basePrice * input.quantity;
39 
40 let discountRate = USER_TYPE_DISCOUNTS[input.userType];
41 
42 if (input.discountCode) {
43 const codeDiscount = DISCOUNT_CODES[input.discountCode] ?? 0;
44 // Discounts stack (additive, not multiplicative)
45 discountRate = Math.min(discountRate + codeDiscount, 0.5);
46 }
47 
48 const discount = subtotal * discountRate;
49 const afterDiscount = subtotal - discount;
50 const tax = afterDiscount * TAX_RATE;
51 const total = afterDiscount + tax;
52 
53 return {
54 subtotal: Math.round(subtotal * 100) / 100,
55 discount: Math.round(discount * 100) / 100,
56 tax: Math.round(tax * 100) / 100,
57 total: Math.round(total * 100) / 100,
58 };
59}

Claude Code'a verince ürettiği testler:

typescript
1// src/lib/pricing.test.ts
2import { describe, it, expect } from "vitest";
3import { calculatePrice } from "./pricing";
4 
5describe("calculatePrice", () => {
6 describe("temel hesaplama", () => {
7 it("misafir kullanıcı için doğru hesaplama yapmalı", () => {
8 const result = calculatePrice({
9 basePrice: 100,
10 quantity: 2,
11 userType: "guest",
12 });
13 
14 expect(result.subtotal).toBe(200);
15 expect(result.discount).toBe(0);
16 expect(result.tax).toBe(36);
17 expect(result.total).toBe(236);
18 });
19 
20 it("üye kullanıcı için %5 indirim uygulamalı", () => {
21 const result = calculatePrice({
22 basePrice: 100,
23 quantity: 1,
24 userType: "member",
25 });
26 
27 expect(result.discount).toBe(5);
28 expect(result.total).toBe(111.65);
29 });
30 
31 it("premium kullanıcı için %10 indirim uygulamalı", () => {
32 const result = calculatePrice({
33 basePrice: 100,
34 quantity: 1,
35 userType: "premium",
36 });
37 
38 expect(result.discount).toBe(10);
39 expect(result.total).toBe(106.2);
40 });
41 });
42 
43 describe("indirim kodu", () => {
44 it("geçerli indirim kodu uygulamalı", () => {
45 const result = calculatePrice({
46 basePrice: 100,
47 quantity: 1,
48 discountCode: "SAVE20",
49 userType: "guest",
50 });
51 
52 expect(result.discount).toBe(20);
53 });
54 
55 it("kullanıcı tipi ve indirim kodu toplamalı (additive)", () => {
56 const result = calculatePrice({
57 basePrice: 100,
58 quantity: 1,
59 discountCode: "SAVE10",
60 userType: "member",
61 });
62 
63 // member %5 + SAVE10 %10 = %15 toplam
64 expect(result.discount).toBe(15);
65 });
66 
67 it("toplam indirim %50yi geçmemeli (cap)", () => {
68 const result = calculatePrice({
69 basePrice: 100,
70 quantity: 1,
71 discountCode: "SAVE20",
72 userType: "premium",
73 });
74 
75 // premium %10 + SAVE20 %20 = %30, cap yok bu vakada
76 expect(result.discount).toBe(30);
77 });
78 
79 it("premium + SAVE20 => %30, save20 + büyük indirim cap'i test et", () => {
80 // %10 + %20 + %20 gibi bir senaryo için cap test
81 // SAVE20 kodu ile premium: 0.10 + 0.20 = 0.30 < 0.50, cap'e takılmaz
82 const result = calculatePrice({
83 basePrice: 1000,
84 quantity: 1,
85 discountCode: "SAVE20",
86 userType: "premium",
87 });
88 
89 expect(result.discount).toBe(300);
90 expect(result.discount / result.subtotal).toBeLessThanOrEqual(0.5);
91 });
92 
93 it("geçersiz indirim kodu 0 indirim uygulamalı", () => {
94 const result = calculatePrice({
95 basePrice: 100,
96 quantity: 1,
97 discountCode: "INVALID_CODE",
98 userType: "guest",
99 });
100 
101 expect(result.discount).toBe(0);
102 });
103 });
104 
105 describe("hata durumları", () => {
106 it("negatif fiyat için hata fırlatmalı", () => {
107 expect(() =>
108 calculatePrice({
109 basePrice: -10,
110 quantity: 1,
111 userType: "guest",
112 })
113 ).toThrow("Base price cannot be negative");
114 });
115 
116 it("sıfır miktar için hata fırlatmalı", () => {
117 expect(() =>
118 calculatePrice({
119 basePrice: 100,
120 quantity: 0,
121 userType: "guest",
122 })
123 ).toThrow("Quantity must be positive");
124 });
125 
126 it("negatif miktar için hata fırlatmalı", () => {
127 expect(() =>
128 calculatePrice({
129 basePrice: 100,
130 quantity: -1,
131 userType: "guest",
132 })
133 ).toThrow("Quantity must be positive");
134 });
135 });
136 
137 describe("edge cases", () => {
138 it("sıfır fiyat geçerli olmalı", () => {
139 const result = calculatePrice({
140 basePrice: 0,
141 quantity: 1,
142 userType: "guest",
143 });
144 
145 expect(result.total).toBe(0);
146 });
147 
148 it("çok büyük sayılar için doğru yuvarlama yapmalı", () => {
149 const result = calculatePrice({
150 basePrice: 99.99,
151 quantity: 3,
152 userType: "guest",
153 });
154 
155 expect(result.subtotal).toBe(299.97);
156 expect(result.total).toBe(353.96);
157 });
158 
159 it("1 miktarında doğru hesaplama yapmalı", () => {
160 const result = calculatePrice({
161 basePrice: 100,
162 quantity: 1,
163 userType: "guest",
164 });
165 
166 expect(result.subtotal).toBe(100);
167 });
168 });
169});

3. Cursor Test Agent Workflow

Cursor'un Composer (Ctrl+I) özelliği, test dosyası olmayan bir source file seçince otomatik test üretiyor. Ama asıl güç, Chat modunda iteratif çalışmak.

Cursor workflow:

  1. Source file'ı açın
  2. Sağ panel Chat'te şunu yazın: "Bu dosya için kapsamlı Vitest testleri yaz, coverage'ı maximize et"
  3. Üretilen testi cmd+K ile dosyaya uygulayın
  4. vitest run --coverage çalıştırın
  5. Eksik coverage görünce Chat'e geri dönün: "şu satırlar hala covered değil: [satır numaraları], eksik testleri ekle"

Cursor'a özel prompt teknikleri:

swift
1# Bağlamı genişlet: birden fazla dosya seç
2"@pricing.ts ve @discount-rules.ts birlikte göz önünde bulundurarak
3integration testleri yaz"
4 
5# Test fix isteme
6"@pricing.test.ts — 3. test failing, error: [hata mesajı]
7Testi düzelt ama mantığı değiştirme"
8 
9# Refactor sonrası test güncelleme
10"pricing.ts refactor edildi. Testleri yeni implementasyona uyarla,
11test logic'i koru"

4. Edge Case Mining Teknikleri

AI'nın en değerli katkısı edge case bulmak. Manuel olarak gözden kaçırdığınız senaryoları AI sistematik şekilde buluyor.

Standart edge case kategorileri:

typescript
1// AI'a verilen prompt kategorileri
2 
3// 1. Boundary Values (Sınır Değerler)
4// - MIN değer (0, -1, MIN_SAFE_INTEGER)
5// - MAX değer (Number.MAX_SAFE_INTEGER, MAX_ARRAY_LENGTH)
6// - Tam sınır (0 vs -1, length vs length-1)
7 
8// 2. Null/Undefined
9// - null input
10// - undefined input
11// - optional field missing vs null
12// - empty object {}
13// - empty array []
14 
15// 3. String Edge Cases
16// - empty string ""
17// - whitespace only " "
18// - very long string (1000+ chars)
19// - special characters <>&"'
20// - unicode characters (emoji, RTL text)
21// - SQL injection patterns
22// - XSS patterns (not for security test, for type safety)
23 
24// 4. Number Edge Cases
25// - 0 ve -0 (evet, JavaScript'te farklı!)
26// - NaN
27// - Infinity ve -Infinity
28// - Float precision (0.1 + 0.2)
29// - Very small decimal (0.001)
30 
31// 5. Async Edge Cases
32// - Promise resolve
33// - Promise reject
34// - Timeout/AbortSignal
35// - Multiple concurrent calls
36// - Race condition simulation

Gerçek edge case örnekleri:

typescript
1// AI'ın bulduğu edge case (ben kaçırmıştım)
2it("0 ve -0 aynı davranmalı", () => {
3 // JavaScript'te 0 === -0 true döner
4 // Ama Object.is(0, -0) false döner
5 // calculatePrice'a -0 geçilirse ne olur?
6 const result = calculatePrice({
7 basePrice: -0, // Negatif sıfır!
8 quantity: 1,
9 userType: "guest",
10 });
11 // -0 < 0 false döner, bu yüzden hata fırlatmaz
12 // Ama subtotal -0 * 1 = -0 olur... sorunlu!
13 expect(result.total).toBe(0);
14});
15 
16it("float precision sorunu olmamalı", () => {
17 // 0.1 + 0.2 = 0.30000000000000004 sorunu
18 const result = calculatePrice({
19 basePrice: 0.1,
20 quantity: 3,
21 userType: "guest",
22 });
23 // Math.round ile çözülmüş, ama test ile doğrulayalım
24 expect(result.subtotal).toBe(0.3);
25 expect(result.subtotal).not.toBe(0.30000000000000004);
26});

5. Mocking Stratejileri

AI en çok mocking konusunda değer katıyor. Dependency injection pattern olmayan legacy kodu test etmek için AI akıllı mock stratejileri üretiyor.

typescript
1// Dependency Injection ile testable kod
2// Önce: direkt import, test edilemez
3import { sendEmail } from "../services/email";
4 
5export async function processOrder(order: Order) {
6 // ... işlem
7 await sendEmail(order.userEmail, "Order confirmed");
8}
9 
10// Sonra: DI pattern, test edilebilir
11export async function processOrder(
12 order: Order,
13 emailService: { send: (to: string, subject: string) => Promise } = defaultEmailService
14) {
15 // ... işlem
16 await emailService.send(order.userEmail, "Order confirmed");
17}
typescript
1// AI üretimli mock örneği
2import { describe, it, expect, vi, beforeEach } from "vitest";
3import { processOrder } from "./order-processor";
4 
5describe("processOrder", () => {
6 const mockEmailService = {
7 send: vi.fn().mockResolvedValue(undefined),
8 };
9 
10 beforeEach(() => {
11 vi.clearAllMocks();
12 });
13 
14 it("başarılı sipariş sonrası email gönderilmeli", async () => {
15 const order = {
16 userEmail: "[email protected]",
17 items: [{ productId: "prod-1", quantity: 2 }],
18 };
19 
20 await processOrder(order, mockEmailService);
21 
22 expect(mockEmailService.send).toHaveBeenCalledOnce();
23 expect(mockEmailService.send).toHaveBeenCalledWith(
25 "Order confirmed"
26 );
27 });
28 
29 it("email gönderimi başarısız olsa bile sipariş işlenmeli", async () => {
30 mockEmailService.send.mockRejectedValueOnce(new Error("SMTP error"));
31 
32 const order = {
33 userEmail: "[email protected]",
34 items: [{ productId: "prod-1", quantity: 1 }],
35 };
36 
37 // Email hatası siparişi patlatmamalı
38 await expect(processOrder(order, mockEmailService)).resolves.not.toThrow();
39 });
40 
41 it("email servisi hiç çağrılmasa ne olur? (empty items)", async () => {
42 const order = {
43 userEmail: "[email protected]",
44 items: [],
45 };
46 
47 // Boş sipariş — email gönderilmemeli mi? Business logic'e göre değişir
48 // AI bu soruyu da işaret ediyor: "Bu case'de ne olması gerektiğini belirtin"
49 await processOrder(order, mockEmailService);
50 
51 // Beklentiyi business logic'e göre belirle
52 expect(mockEmailService.send).not.toHaveBeenCalled();
53 });
54});

Module mock için vi.mock:

typescript
1// External module mock
2vi.mock("../services/analytics", () => ({
3 track: vi.fn(),
4 identify: vi.fn(),
5}));
6 
7// Partial mock (bazı fonksiyonları gerçek tut)
8vi.mock("../utils/date", async (importOriginal) => {
9 const actual = await importOriginal();
10 return {
11 ...actual,
12 now: vi.fn(() => new Date("2026-04-18T12:00:00Z")),
13 };
14});

6. Coverage Analizi ve Optimizasyon

AI coverage raporunu analiz edip hangi testlerin öncelikli olduğunu söyleyebiliyor.

bash
1# Vitest coverage raporu
2npx vitest run --coverage --coverage.reporter=json
3 
4# Raporu AI'a ver ve analiz ettir
typescript
1// vitest.config.ts - coverage konfigürasyonu
2import { defineConfig } from "vitest/config";
3 
4export default defineConfig({
5 test: {
6 coverage: {
7 provider: "v8",
8 reporter: ["text", "json", "html"],
9 thresholds: {
10 lines: 90,
11 branches: 85,
12 functions: 90,
13 statements: 90,
14 },
15 exclude: [
16 "**/*.test.ts",
17 "**/*.spec.ts",
18 "**/types/**",
19 "**/mocks/**",
20 "**/__fixtures__/**",
21 ],
22 },
23 },
24});

Line coverage vs Branch coverage farkı:

typescript
1function getDiscount(userType: string): number {
2 if (userType === "premium") { // Branch 1: true/false
3 return 0.2;
4 }
5 return 0; // Branch 1 false: Bu satır çalışıyor
6}
7 
8// Line coverage: Her satır en az bir kez çalıştı mı? %100 olabilir
9// Branch coverage: Her if'in hem true hem false dalı test edildi mi?
10 
11// Sadece şu test varsa:
12it("premium indirim", () => {
13 expect(getDiscount("premium")).toBe(0.2);
14});
15 
16// Line coverage: %100 (tüm satırlar çalıştı)
17// Branch coverage: %50 (sadece true branch test edildi)
18// False branch (non-premium) hiç test edilmedi!

AI'a coverage raporunu verince şunu söylüyor: "Branch coverage %82, özellikle şu satırlardaki false branch'ler eksik. Şu testleri ekle..."


7. Property-Based Testing ile AI

Property-based testing, belirli input/output örnekleri yerine "bu input tipi için bu property her zaman geçerli olmalı" der. AI bu test türünde özellikle güçlü.

typescript
1// fast-check ile property-based testing
2import { describe, it, expect } from "vitest";
3import * as fc from "fast-check";
4import { calculatePrice } from "./pricing";
5 
6describe("calculatePrice - property based", () => {
7 it("total her zaman pozitif olmalı", () => {
8 fc.assert(
9 fc.property(
10 fc.float({ min: 0, max: 10000, noNaN: true }),
11 fc.integer({ min: 1, max: 100 }),
12 fc.constantFrom("guest", "member", "premium" as const),
13 (basePrice, quantity, userType) => {
14 const result = calculatePrice({ basePrice, quantity, userType });
15 return result.total >= 0;
16 }
17 )
18 );
19 });
20 
21 it("discount asla subtotal'dan büyük olmamalı", () => {
22 fc.assert(
23 fc.property(
24 fc.float({ min: 0.01, max: 10000, noNaN: true }),
25 fc.integer({ min: 1, max: 100 }),
26 fc.constantFrom("guest", "member", "premium" as const),
27 fc.option(fc.constantFrom("SAVE10", "SAVE20", "WELCOME")),
28 (basePrice, quantity, userType, discountCode) => {
29 const result = calculatePrice({
30 basePrice,
31 quantity,
32 userType,
33 discountCode: discountCode ?? undefined,
34 });
35 return result.discount <= result.subtotal;
36 }
37 )
38 );
39 });
40 
41 it("subtotal her zaman basePrice * quantity olmalı", () => {
42 fc.assert(
43 fc.property(
44 fc.float({ min: 0, max: 1000, noNaN: true }),
45 fc.integer({ min: 1, max: 50 }),
46 (basePrice, quantity) => {
47 const result = calculatePrice({
48 basePrice,
49 quantity,
50 userType: "guest",
51 });
52 const expected = Math.round(basePrice * quantity * 100) / 100;
53 return Math.abs(result.subtotal - expected) < 0.01;
54 }
55 )
56 );
57 });
58});

AI'a "Bu fonksiyon için hangi property'ler her zaman geçerli olmalı?" diye sorduğumda verdiği cevap:

  1. total = subtotal - discount + tax (matematiksel tutarlılık)
  2. 0 <= discountRate <= 0.5 (cap garantisi)
  3. tax = (subtotal - discount) * TAX_RATE (vergi hesabı)
  4. total >= 0 her zaman
  5. Aynı input => aynı output (pure function)

Bu property'lerin her biri bir test grubuna dönüşüyor.


8. Snapshot Test Review

Snapshot testler değişip değişmediğini test eder, doğruluğu değil. AI snapshot'ları review edip "bu snapshot mantıklı mı?" sorusunu cevaplayabiliyor.

typescript
1// Component snapshot test
2import { render } from "@testing-library/react";
3import { PriceDisplay } from "./PriceDisplay";
4 
5describe("PriceDisplay snapshot", () => {
6 it("fiyat gösterimi değişmemeli", () => {
7 const { container } = render(
8
9 price={99.99}
10 currency="TRY"
11 discount={10}
12 />
13 );
14 
15 expect(container).toMatchSnapshot();
16 });
17});

AI'a snapshot dosyasını verince şunları söylüyor:

  • "Bu snapshot'ta hardcoded class name'ler var, CSS değişince gereksiz fail olur"
  • "data-testid kullanın, snapshot'ı semantic HTML'e göre alın"
  • "Tarih/zaman içeren snapshot'lar flaky, mock'layın"

AI önerisiyle düzeltilmiş:

typescript
1it("fiyat bilgileri doğru render edilmeli", () => {
2 const { getByTestId } = render(
3
4 );
5 
6 // Snapshot yerine semantic assertion
7 expect(getByTestId("price-amount")).toHaveTextContent("99,99");
8 expect(getByTestId("price-currency")).toHaveTextContent("TRY");
9 expect(getByTestId("discount-badge")).toHaveTextContent("%10");
10});

9. Flaky Test Tespiti

Flaky test: Bazen geçen, bazen geçmeyen test. CI'da güvensizlik yaratır, "tekrar çalıştır" kültürü oluşturur. AI flaky test pattern'lerini tanıyor.

Yaygın flaky test nedenleri:

typescript
1// PROBLEM 1: Date.now() kullanımı
2it("created_at bugün olmalı", () => {
3 const user = createUser("[email protected]");
4 // 23:59'da çalışırsa bugün, 00:01'de çalışırsa dün!
5 expect(user.createdAt.toDateString()).toBe(new Date().toDateString());
6});
7 
8// ÇÖZÜM: Date mock
9beforeEach(() => {
10 vi.useFakeTimers();
11 vi.setSystemTime(new Date("2026-04-18T12:00:00Z"));
12});
13 
14afterEach(() => {
15 vi.useRealTimers();
16});
17 
18// PROBLEM 2: Order-dependent test
19let sharedState = { count: 0 };
20 
21it("count 0 olmalı", () => {
22 expect(sharedState.count).toBe(0); // Test sırası değişince fail!
23});
24 
25it("count increment", () => {
26 sharedState.count++;
27 expect(sharedState.count).toBe(1);
28});
29 
30// ÇÖZÜM: Her test independent
31describe("counter", () => {
32 let state: { count: number };
33 
34 beforeEach(() => {
35 state = { count: 0 }; // Her test için sıfırla
36 });
37 
38 it("count 0 olmalı", () => {
39 expect(state.count).toBe(0);
40 });
41});
42 
43// PROBLEM 3: Async timing
44it("debounced search çalışmalı", async () => {
45 triggerSearch("query");
46 // 300ms debounce var ama setTimeout'u beklemiyoruz
47 expect(mockSearchFn).toHaveBeenCalled(); // Flaky!
48});
49 
50// ÇÖZÜM: Fake timers
51it("debounced search çalışmalı", async () => {
52 vi.useFakeTimers();
53 triggerSearch("query");
54 await vi.runAllTimersAsync();
55 expect(mockSearchFn).toHaveBeenCalled();
56 vi.useRealTimers();
57});

AI'a CI log'larını verince "bu testler neden flaky?" sorusunu analiz ediyor ve yukarıdaki pattern'leri tespit ediyor.


10. Team Workflow ve Standartlar

AI destekli test yazımını takım genelinde standartlaştırmak için:

typescript
1// .cursor/rules veya CLAUDE.md'e eklenecek test standartları
2 
3/*
4TEST STANDARTLARI:
51. Her test dosyası: describe > describe > it hiyerarşisi
62. Test isimlendirme: "[fonksiyon] - [durum] - [beklenti]" şeklinde
73. AAA pattern zorunlu: Arrange, Act, Assert
84. Her test bağımsız: beforeEach ile state sıfırla
95. Mock'lar dosya üstünde tanımla, test içinde değil
106. Assertion mesajları açıklayıcı ol
11*/
12 
13// Takım için test template (snippet olarak kaydet)
14describe("[UNIT_NAME]", () => {
15 // Arrange - shared setup
16 const mockDep = {
17 method: vi.fn(),
18 };
19 
20 beforeEach(() => {
21 vi.clearAllMocks();
22 });
23 
24 describe("[happy path]", () => {
25 it("[beklenen davranış]", () => {
26 // Arrange
27 const input = {};
28 
29 // Act
30 const result = unitUnderTest(input);
31 
32 // Assert
33 expect(result).toEqual({});
34 });
35 });
36 
37 describe("[error cases]", () => {
38 it("[hata koşulu] için [beklenen hata]", () => {
39 expect(() => unitUnderTest(invalidInput)).toThrow("[error message]");
40 });
41 });
42 
43 describe("[edge cases]", () => {
44 it("[edge case açıklaması]", () => {
45 // ...
46 });
47 });
48});

AI test review checklist:

swift
1Test review için Claude'a ver ve şunu sor:
2"Bu test suite'i review et:
3- Her test gerçekten bir şey test ediyor mu?
4- False positive risk var mı? (her zaman geçen ama geçmemesi gereken)
5- Coverage'da gözden kaçan alan var mı?
6- Mock'lar doğru kullanılmış mı?
7- Test isimleri davranışı açıklıyor mu?"

ALTIN İPUCU

Bu yazının en değerli bilgisi

Bu ipucu, yazının en önemli çıkarımını içeriyor.

bash
1# Stryker kurulum
2npm install --save-dev @stryker-mutator/core @stryker-mutator/vitest-runner
3 
4# stryker.config.js
5export default {
6 testRunner: "vitest",
7 mutate: ["src/**/*.ts", "!src/**/*.test.ts"],
8 reporters: ["html", "json"],
9};
10 
11# Çalıştır
12npx stryker run

Easter Egg

Gizli bir bilgi buldun!

Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?

swift
1// PricingService.swift
2struct PricingService {
3 func calculateDiscount(userType: UserType, amount: Double) -> Double {
4 switch userType {
5 case .guest:
6 return 0
7 case .member:
8 return amount * 0.05
9 case .premium:
10 return amount * 0.10
11 }
12 }
13}
14 
15// AI üretimli XCTest
16import XCTest
17@testable import MyApp
18 
19final class PricingServiceTests: XCTestCase {
20 var sut: PricingService!
21 
22 override func setUp() {
23 super.setUp()
24 sut = PricingService()
25 }
26 
27 override func tearDown() {
28 sut = nil
29 super.tearDown()
30 }
31 
32 // MARK: - Guest User
33 
34 func test_calculateDiscount_withGuestUser_returnsZero() {
35 let discount = sut.calculateDiscount(userType: .guest, amount: 100)
36 XCTAssertEqual(discount, 0, accuracy: 0.001)
37 }
38 
39 // MARK: - Member User
40 
41 func test_calculateDiscount_withMemberUser_returnsFivePercent() {
42 let discount = sut.calculateDiscount(userType: .member, amount: 100)
43 XCTAssertEqual(discount, 5.0, accuracy: 0.001)
44 }
45 
46 // MARK: - Premium User
47 
48 func test_calculateDiscount_withPremiumUser_returnsTenPercent() {
49 let discount = sut.calculateDiscount(userType: .premium, amount: 100)
50 XCTAssertEqual(discount, 10.0, accuracy: 0.001)
51 }
52 
53 // MARK: - Edge Cases
54 
55 func test_calculateDiscount_withZeroAmount_returnsZero() {
56 let discount = sut.calculateDiscount(userType: .premium, amount: 0)
57 XCTAssertEqual(discount, 0, accuracy: 0.001)
58 }
59 
60 func test_calculateDiscount_withLargeAmount_calculatesCorrectly() {
61 let discount = sut.calculateDiscount(userType: .member, amount: 99999)
62 XCTAssertEqual(discount, 4999.95, accuracy: 0.01)
63 }
64}

Okuyucu Ödülü

Claude Code Swift/XCTest için de test üretiyor:

Sonuç

AI destekli test yazımı 2026'da olgunlaştı. Claude Code ve Cursor kombinasyonu ile test yazma hızı 4x artıyor, coverage %94'e ulaşıyor, flaky test oranı dramatik düşüyor.

En önemli çıkarım: AI testi yazıyor, siz karar veriyorsunuz. Hangi edge case'lerin önemli olduğunu, hangi business logic'in kritik olduğunu siz biliyorsunuz. AI bu kararları destekliyor, yerine geçmiyor.

Test yazmaya başlamak için iyi bir yer: Coverage raporu alın, en düşük coverage'lı 3 dosyayı seçin, AI'a verin ve "bu dosya için kapsamlı testler yaz" deyin. Hafta sonu olmadan coverage'ınız %90 üstüne çıkar.

AI kodlama workflow'unuzu optimize etmek istiyorsanız Claude Computer Use API production rehberine bakabilirsiniz. AI araçlarında yapılan yaygın hataları öğrenmek için AI kod yazmada 10 yanılgı yazısını inceleyebilirsiniz. Zorlu task'larda Claude Opus 4.7'nin ne zaman devreye alınması gerektiğini merak ediyorsanız bu rehber işinize yarayacak.

Faydalı kaynaklar:

Etiketler

#AI Testing#Unit Test#Claude Code#Cursor#Coverage#TDD#2026
Muhittin Çamdalı

Muhittin Çamdalı

Senior iOS Developer

12+ yıllık deneyime sahip iOS Developer. Swift, SwiftUI ve modern iOS mimarileri konusunda uzman. Apple platformlarında performanslı ve kullanıcı dostu uygulamalar geliştiriyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.

Paylaş

Bunu da begenebilirsiniz