Bölüm 11: Test ve Hata Ayıklama
Çoktan Seçmeli · Giriş
Soru 1: Hata, Kusur, Arıza
Programcı dizinin en büyük öğesini bulan metotta başlangıç değerini yanlışlıkla 0 seçti; tüm sıcaklıklar negatif olduğunda ekranda “En yüksek: 0” göründü. Bu olayda arıza (failure) hangisidir?
B: Hata (error) programcının yanılgısıdır; kusur (defect) bu yanılgının koda yansıyan yanlış satırıdır (int enb = 0;); arıza (failure) çalışırken görülen yanlış davranıştır. Test arızayı ortaya çıkarır, hata ayıklama kusuru bulur, düzeltme hatayı giderir.
Çoktan Seçmeli · Test Türleri
Soru 2: Hangi Seviye?
Hesap alt sistemi tarihleri gün/ay/yıl, veri tabanı alt sistemi yıl-ay-gün biçiminde bekliyor; ikisi tek başına doğru çalışıyor ama birlikte yanlış kayıt oluşuyor. Bu sorunu en doğrudan hangi test seviyesi yakalar?
C: Alt sistem testi, alt sistemlerin birleştiği arayüzleri (iletişim, veri biçimi) sınar. Birim testleri her parçayı tek başına sınar; burada ikisi de geçer. Sistem testi bütün sistemi işlevsel ve işlevsel olmayan gereksinimlerle, kabul testi müşteriyle gerçek veri üzerinde sınar; sorunu daha geç ve pahalı yakalarlar.
Çıktı Sorusu · Birim Testi ve JUnit
Soru 3: Hangileri Geçer?
JUnit 5 bu beş testi çalıştırıyor. Hangileri geçer (✔), hangileri kalır (✘)? Kalanların nedeni nedir?
@Test void a() { assertEquals(new int[] {1, 2}, new int[] {1, 2}); } @Test void b() { assertArrayEquals(new int[] {1, 2}, new int[] {1, 2}); } @Test void c() { assertEquals(1.0 / 3, 0.333, 0.001); } @Test void d() { assertSame(new String("x"), "x"); } @Test void e() { assertEquals("x", new String("x")); }
Cevabı göster
a✘:assertEqualsdiziler içinequalskullanır, o da referans karşılaştırır: iki ayrı dizi eşit değildir. İleti iki dizinin de[1, 2]olduğunu gösterir ama adresleri farklıdır.b✔:assertArrayEqualsöğe öğe karşılaştırır.c✔: |0,3333… − 0,333| ≈ 0,0003 ≤ 0,001.d✘:assertSameaynı nesneyi (==) ister;new Stringyeni nesnedir.e✔:assertEqualsString'lerde içerik karşılaştırır.
Çağrılar JUnit 5.11'in Assertions sınıfıyla denendi.
Adım Adım Çalıştırma · Birim Testi ve JUnit
Soru 4: Test Nesneleri ve static Alan
JUnit bu sınıftaki testleri t1, t2 sırasıyla çalıştırdı. Her test için ne olur, sonuç ne olur? Test sırası ters olsaydı ne değişirdi?
t1 için yeni bir test nesnesi (#1) oluşur; örnek alanı n = 0. static s sınıfa aittir, tek kopyadır.
@BeforeEach her testten önce çalışır.
n = 1: geçer.
t2 için yepyeni nesne (#2): n yine 0'dan başlar; s ise 1'de kalmıştır.
@BeforeEach: s 2, n 1.
s = 2: kalır. Sıra ters olsaydı t2 s = 1 ile geçer, t1 de geçerdi: sonuç test sırasına bağlı. Testler static durum paylaşmamalı; örnek alanları her test için sıfırlanır.
class SiraTest {
static int s = 0;
int n = 0;
@BeforeEach
void hazirla() {
s++;
n++;
}
@Test
void t1() {
assertEquals(1, n);
}
@Test
void t2() {
assertEquals(1, s);
}
}Çoktan Seçmeli · Birim Testi ve JUnit
Soru 5: Alt Sınıf İstisnası
Integer.parseInt("abc") NumberFormatException fırlatır. Bu test ne olur?
@Test
void harfliSayi() {
assertThrows(IllegalArgumentException.class,
() -> Integer.parseInt("abc"));
}D: assertThrows, fırlatılan istisna beklenen tipte ya da onun alt tipinde ise geçer ve istisna nesnesini döndürür (burada NumberFormatException, iletisi For input string: "abc"). Tam tip istenirse assertThrowsExactly kullanılır. Lambda değer döndüren bir ifade de olabilir; değer yok sayılır.
Çoktan Seçmeli · Birim Testi ve JUnit
Soru 6: Yaşam Döngüsü Sayıları
Bir test sınıfında üç @Test metodu, birer tane de @BeforeAll, @BeforeEach, @AfterEach ve @AfterAll metodu var. Testlerden biri kalıyor. @BeforeAll, @BeforeEach ve @AfterEach metotları sırasıyla kaç kez çalışır?
A: @BeforeAll (static) tüm testlerden önce bir kez çalışır. @BeforeEach ve @AfterEach her test için birer kez çalışır; @AfterEach test kalsa bile çalışır, temizlik işi bu yüzden oraya konur. @AfterAll da en sonda bir kez çalışır.
Klasik Soru · Birim Testi ve JUnit
Soru 7: Bilet Fiyatı için Sınır Değerleri
Metot için sınır değer analiziyle bir test girdisi kümesi seçin ve her girdi için beklenen sonucu yazın.
static int bilet(int yas) { if (yas < 0 || yas > 120) throw new IllegalArgumentException( "Geçersiz yaş: " + yas); if (yas < 7) return 0; if (yas < 18) return 50; if (yas >= 65) return 60; return 100; }
Cevabı göster
| yas | −1 | 0 | 6 | 7 | 17 | 18 | 64 | 65 | 120 | 121 |
|---|---|---|---|---|---|---|---|---|---|---|
| beklenen | istisna | 0 | 0 | 50 | 50 | 100 | 100 | 60 | 60 | istisna |
Her aralığın iki ucu ve geçersiz bölgenin ilk değerleri seçildi; hatalar en çok < ile <= karışınca, tam bu değerlerde çıkar. Aynı aralıktaki 30, 40 gibi değerler aynı denklik sınıfındadır, biri yeterlidir. Uygulaması: @ParameterizedTest + @CsvSource({"0, 0", "6, 0", "7, 50", …}) ve aralık dışı için iki assertThrows. Tablo metot çalıştırılarak doğrulandı.
Hata Bulma · Birim Testi ve JUnit
Soru 8: Artık Yıl Testi Kalıyor
Parametreli test bu metodu sınıyor. Hangi satır kalır, JUnit hangi iletiyi verir? Kusuru düzeltin.
static boolean artikYil(int y) { return y % 4 == 0 && y % 100 != 0; } @ParameterizedTest @CsvSource({"2024, true", "1900, false", "2000, true", "2023, false"}) void artikYillar(int yil, boolean beklenen) { assertEquals(beklenen, artikYil(yil)); }
Cevabı göster
✔ [1] 2024, true ✔ [2] 1900, false ✘ [3] 2000, true expected: <true> but was: <false> ✔ [4] 2023, false
Kusur: 400'e bölünen yüzyıl yılları (2000) da artık yıldır, metot bu kuralı unutuyor. Düzeltme:
return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0;
Yalnızca 2024 ve 2023 seçilseydi testler geçer, kusur gizli kalırdı: 1900 ve 2000 bu kuralın sınır durumlarıdır.
Adım Adım Çalıştırma · Birim Testi ve JUnit
Soru 9: Test İçinde İstisna
BankaHesabi'nda cek, bakiyeden fazla miktar için IllegalStateException("Yetersiz bakiye") fırlatır. Bu test çalışınca hangi satırlar çalışır, sonuç ne olur?
Hazırla: hesap 100 ile açılır.
yatir(50): 150.
Doğrulama geçer; test devam eder.
200 > 150: cek istisna fırlatır. Test metodunda yakalanmadığı için metot burada biter.
Son doğrulama ve println hiç çalışmaz: “bitti” yazılmaz. Beklenmeyen istisna testi düşürür. İstisna beklenen davranışsa assertThrows ile sınanır; bir testte tek davranış sınanması da hatanın yerini netleştirir.
@Test
void yatirVeCek() {
BankaHesabi h = new BankaHesabi(100);
h.yatir(50);
assertEquals(150.0, h.getBakiye(), 0.001);
h.cek(200);
assertEquals(-50.0, h.getBakiye(), 0.001);
System.out.println("bitti");
}Çoktan Seçmeli · Regresyon ve Otomasyon
Soru 10: Kararsız Test
Şu testlerden hangisi aynı kodla bazen geçip bazen kalabilir (flaky)?
B: Tohumsuz new Random() her çalıştırmada farklı sayı üretir: belirli bir değeri beklemek zamanla 1/6 olasılıkla geçer. Sabit tohum (42) her seferinde aynı diziyi verir, test belirleyicidir. 1–6 aralığını denetlemek her zarda doğrudur; aynı tohumlu iki zar da hep aynı sonucu verir.
Çoktan Seçmeli · Regresyon ve Otomasyon
Soru 11: TDD Döngüsü
Test güdümlü geliştirmede (TDD) bir özellik için doğru adım sırası hangisidir?
C: TDD: kırmızı (henüz olmayan davranış için kalan ya da derlenmeyen test), yeşil (geçiren en basit kod), yeniden düzenleme (davranışı testler korurken kodu temizlemek). Böylece her satır kod bir testle doğar ve regresyon testleri kendiliğinden birikir.
Adım Adım Çalıştırma · Hata Ayıklama
Soru 12: Hata Ayıklayıcıyla Ortalama
ortalama, {1, 2} için 1.5 yerine 1.0 döndürüyor. 10. satırda kesme noktasında durulmuşken şu komutlar veriliyor: Step Into, Step Over, 7. satıra kesme noktası + Resume, Evaluate, Step Out, Step Over. Her adımda nerede olunur, değişkenler ne olur?
Kesme noktası: 10. satır henüz çalışmadı.
Step Into: ortalama'nın içine girildi; yığına yeni çerçeve eklendi. t henüz yok.
Step Over: t = 0 çalıştı.
Resume: döngü bitti, yeni kesme noktasında durdu. t = 3 doğru.
Evaluate: t / d.length = 1. İki int bölünüyor: tam sayı bölmesi! Kusur bulundu; (double) t / d.length = 1.5.
Step Out: metot bitti, 1 → 1.0 olarak döndü; main'e dönüldü, atama yapıldı.
Step Over: 1.0 yazıldı. Düzeltmeden önce {1, 2} → 1.5 bekleyen bir birim testi yazılır; düzeltmeden sonra yeşile döner.
public class Ort { static double ortalama(int[] d) { int t = 0; for (int v : d) { t += v; } return t / d.length; } public static void main(String[] args) { double o = ortalama(new int[] {1, 2}); System.out.println(o); } }
Çıktı Sorusu · Hata Ayıklama
Soru 13: assert Açık ve Kapalı
Program önce java Kok, sonra java -ea Kok ile çalıştırılıyor. İki çalıştırmada ne görülür?
public class Kok { static int kok(int x) { assert x >= 0 : "negatif: " + x; return (int) Math.sqrt(x); } public static void main(String[] args) { System.out.println(kok(16)); System.out.println(kok(-4)); } }
Cevabı göster
java Kok:
4 0
Doğrulamalar varsayılan olarak kapalıdır; assert satırı hiç çalışmaz. Math.sqrt(-4) NaN'dır, (int) NaN'ı 0 yapar: hata sessizce geçer.
java -ea Kok:
4 Exception in thread "main" java.lang.AssertionError: negatif: -4
Koşul yanlış: mesajla birlikte AssertionError. assert geliştirme sırasındaki “olmamalı” varsayımları içindir; kullanıcı girdisi istisnayla (IllegalArgumentException) denetlenir, çünkü üretimde assert kapalı olabilir.
Hesap Sorusu · Analiz Araçları
Soru 14: Satır ve Dal Kapsamı
Bu metot için yalnızca assertEquals("düşük", sinif(5)) testi var. Satır kapsamı (5 çalıştırılabilir satır) ve dal kapsamı (2 koşul × doğru/yanlış) yüzde kaçtır? %100 dal kapsamı için hangi testler eklenmeli?
static String sinif(int t) { if (t < 0) { return "geçersiz"; } if (t <= 10) { return "düşük"; } return "yüksek"; }
Cevabı göster
- sinif(5):
t < 0(yanlış) →t <= 10(doğru) →return "düşük". Satır: 3 / 5 = %60 (return "geçersiz"vereturn "yüksek"çalışmadı). - Dal: 1. koşulun yanlış, 2. koşulun doğru dalı: 2 / 4 = %50.
- Eklenecekler:
sinif(-1)→ "geçersiz" (1. koşul doğru) vesinif(20)→ "yüksek" (2. koşul yanlış). Sınırlar için 0 ve 10 da eklenmeli; kapsam %100 olsa bile<=yerine<yazılmasını ancak sinif(10) yakalar.
Çoktan Seçmeli · Analiz Araçları
Soru 15: Statik Analizle Bulunur
Hangisini SpotBugs gibi bir statik analiz aracı, programı çalıştırmadan bulabilir?
D: Statik analiz derlenmiş kodu kalıplara göre inceler: ES_COMPARING_STRINGS_WITH_EQ, sonucu kullanılmayan trim() gibi. Zaman harcanan yeri profil oluşturucu (JFR, VisualVM), dolan heap'i heap dökümü ve Eclipse Memory Analyzer, tarayıcı davranışını Selenium testleri gösterir; bunlar programın çalışmasını gerektirir.
Çıktı Sorusu · Analiz Araçları
Soru 16: switch ve -Xlint
Program ne yazdırır? javac -Xlint:all hangi uyarıları verir?
public class Sw { public static void main(String[] args) { int puan = 2; String s = ""; switch (puan) { case 1: s += "a"; case 2: s += "b"; case 3: s += "c"; break; default: s += "d"; } System.out.println(s); } }
Cevabı göster
bc
puan = 2: case 2'den girilir, break olmadığı için case 3'e düşer (fall-through): "b" + "c". case 3'teki break default'a geçişi durdurur.
Sw.java:7: warning: [fallthrough] possible fall-through into case Sw.java:8: warning: [fallthrough] possible fall-through into case 2 warnings
Düz javac uyarı vermeden derler; -Xlint unutulmuş break'leri çalıştırmadan gösterir.
Çoktan Seçmeli · Analiz Araçları
Soru 17: Neden Toplanmıyor?
Bir sunucu her isteğin 1 MB'lık verisini static List<byte[]> onbellek listesine ekliyor ve hiç çıkarmıyor; bir süre sonra OutOfMemoryError alınıyor. Çöp toplayıcı bu dizileri neden silmiyor?
A: GC yalnızca erişilemeyen nesneleri siler. Static alan programın sonuna kadar yaşar; listedeki her dizi ondan erişilebildiği için toplanmaz: Java'da bellek sızıntısı budur. VisualVM'de GC sonrası dipler sürekli yükselir; heap dökümünü Memory Analyzer'ın Leak Suspects raporu bu listeye bağlar. Çözüm: işi biten veriyi çıkarmak ya da boyutu sınırlı önbellek.