Ders 12 / 12
Hata Yaşam Döngüsü
Hata, kusur ve arıza ayrımı, hata kaydının alanları, önem ile önceliğin ayrılması ve kaydın yaşam döngüsünün durum makinesi olarak modellenip geçiş kapsamının ölçülmesi.
İçindekiler
Bir kâhin sapma gösterdiğinde iş bitmez, başlar. Sapmanın kaydedilmesi, tekrarlanabilir biçimde anlatılması, önceliklendirilmesi, giderilmesi ve giderildiğinin doğrulanması gerekir. Bu adımların her biri bir durumdur ve aralarında yalnız belirli geçişler geçerlidir.
Bu ders o döngüyü kurar. Döngünün kendisi de bir sistemdir ve önceki derslerdeki tekniklerle sınanabilir; son bölümde bu yapılır.
Hata, Kusur ve Arıza
Türkçede de İngilizcede de birbirinin yerine kullanılan üç terim ayrı şeyleri adlandırır.
Hata (error) insanın yaptığı yanlıştır: kuralın yanlış anlaşılması, bir sınırın yanlış okunması, bir operatörün yanlış yazılması. Hata bir üründe değil, bir kişide olur.
Kusur (defect) hatanın üründe bıraktığı izdir: yanlış yazılmış koşul, eksik denetim, yanlış belirtim maddesi. Kusur üründe durur ve kimse çalıştırmasa da oradadır.
Arıza (failure) kusurun dışa vurmasıdır: programın gözlenen davranışının beklenenden sapması. Her kusur arızaya dönüşmez — kusurun bulunduğu dal hiç çalışmıyorsa arıza görünmez.
Ayrım pratikte işe yarar. Bir hata kaydı arızayı anlatır, çünkü gözlenen budur. Düzeltme kusuru hedefler. Kusurun tekrarlanmaması için yapılan iş ise hataya bakar: aynı yanlış anlama başka yerlerde de var mı?
Hata Kaydının Alanları
İyi bir hata kaydının ölçütü tektir: kaydı okuyan başka bir kişi, yazarına hiç soru sormadan arızayı yeniden üretebilmelidir.
Kayıt şu alanları taşır. Kimlik ve başlık: başlık arızayı tek cümlede söyler, gerekçe değil gözlem içerir. Ortam: sürüm, yapılandırma, veri kümesi — arızanın yeniden üretilebilmesi için gereken bağlam. Ön koşul ve adımlar: senaryo yazımı dersindeki biçimin aynısı. Gözlenen sonuç ve beklenen sonuç: ikisi ayrı ayrı yazılır, çünkü farkı okuyan kişi görmelidir. Kanıt: günlük kaydı, çıktı, ekran görüntüsü. İzlenebilirlik bağı: hangi kural, hangi senaryo, hangi sürüm.
En sık atlanan alan beklenen sonuçtur. “Ücret yanlış hesaplanıyor” cümlesi bir kayıt değil, bir şikâyettir; kayıt “gün=0, öğrenci, şube dışı iade için 3 birim hesaplandı, kural gereği 2 birim olmalıydı” der.
Önem ve Öncelik
İki alan sık sık karıştırılır ve karıştırıldığında yanlış iş sırası doğar.
Önem (severity) arızanın etkisinin büyüklüğüdür: veri kaybı mı, yanlış tutar mı, yerleşim bozukluğu mu? Ürünün kendisine bakılarak belirlenir ve büyük ölçüde nesneldir.
Öncelik (priority) düzeltmenin ne zaman yapılacağıdır. Etkiye ek olarak kaç kullanıcıyı etkilediğine, kaçınma yolu olup olmadığına, sürüm takvimine ve düzeltmenin riskine bakar.
İkisi bağımsızdır. Yılda bir çalışan bir raporda veri kaybı yüksek önemli, düşük öncelikli olabilir. Açılış ekranındaki bir yazım yanlışı düşük önemli, yüksek öncelikli olabilir. İki alanı tek alana indiren bir kayıt biçimi, bu iki kararı birbirine karıştırmaya zorlar.
Yaşam Döngüsü Bir Durum Makinesidir
Kaydın izlediği yol serbest değildir. Doğrulanmamış bir kayıt kapatılamaz, kapatılmış bir kayda düzeltme iliştirilemez. Bu kısıtlar bir durum makinesidir.
// dongu.mjs — hata kaydinin durum makinesi export const GECISLER = { yeni: { ata: 'atandi', reddet: 'reddedildi' }, atandi: { duzelt: 'duzeltildi', reddet: 'reddedildi' }, duzeltildi: { dogrula: 'dogrulandi', yenidenAc: 'yenidenAcildi' }, dogrulandi: { kapat: 'kapandi' }, kapandi: { yenidenAc: 'yenidenAcildi' }, reddedildi: { yenidenAc: 'yenidenAcildi' }, yenidenAcildi: { ata: 'atandi' }, }; export const DURUMLAR = Object.keys(GECISLER); export const OLAYLAR = ['ata', 'duzelt', 'dogrula', 'kapat', 'reddet', 'yenidenAc']; export function uygula(durum, olay) { const sonraki = GECISLER[durum]?.[olay]; if (sonraki === undefined) throw new Error(`gecersiz gecis: ${durum} + ${olay}`); return sonraki; }
Makine yazılı olduğunda geçerli ve geçersiz geçişler sayılabilir.
// sayim.mjs — butun durum-olay ciftlerinin sayilmasi import { DURUMLAR, OLAYLAR, GECISLER, uygula } from './dongu.mjs'; let gecerli = 0; let reddedilen = 0; for (const durum of DURUMLAR) { for (const olay of OLAYLAR) { if (GECISLER[durum][olay] !== undefined) { gecerli += 1; continue; } try { uygula(durum, olay); } catch { reddedilen += 1; } } } const toplam = DURUMLAR.length * OLAYLAR.length; console.log(`durum sayisi : ${DURUMLAR.length}`); console.log(`olay sayisi : ${OLAYLAR.length}`); console.log(`tum ciftler : ${toplam}`); console.log(`gecerli gecis : ${gecerli}`); console.log(`gecersiz cift : ${toplam - gecerli}, reddedilen: ${reddedilen}`);
durum sayisi : 7 olay sayisi : 6 tum ciftler : 42 gecerli gecis : 10 gecersiz cift : 32, reddedilen: 32
Kırk iki çiftin yalnız onu geçerlidir; geri kalan otuz ikisi reddedilmelidir ve reddedilmiştir. Bu ikinci sayı çoğu zaman gözden kaçar. Kaydın “doğrulandı” durumuna gelmeden kapatılabilmesi bir kusurdur, ama hiçbir mutlu yol senaryosu bunu göstermez.
Doğrulandı durumunun ayrı bir adım olması da bilinçlidir. Düzeltmeyi yapan kişi kaydı kapatamaz; kapatma yetkisi arızayı bildiren ya da doğrulayan taraftadır. Yeniden test bu adımda koşturulur.
Durum Geçiş Testi
Dördüncü derste ertelenen teknik burada uygulanır. Durum geçiş testi (state transition testing) denenmesi gereken şeyin değerler değil geçişler olduğu durumlarda kullanılır ve ölçüsü geçiş kapsamıdır: geçerli geçişlerin kaçı en az bir kez denendi?
// gecis-kapsami.mjs — iki senaryo kumesinin gecis kapsami import { GECISLER, uygula } from './dongu.mjs'; const TUM_GECISLER = Object.entries(GECISLER) .flatMap(([durum, olaylar]) => Object.keys(olaylar).map((olay) => `${durum}+${olay}`)); function kapsam(yollar) { const gorulen = new Set(); for (const yol of yollar) { let durum = 'yeni'; for (const olay of yol) { gorulen.add(`${durum}+${olay}`); durum = uygula(durum, olay); } } return gorulen; } const MUTLU_YOL = [['ata', 'duzelt', 'dogrula', 'kapat']]; const GENIS_KUME = [ ['ata', 'duzelt', 'dogrula', 'kapat', 'yenidenAc', 'ata'], ['ata', 'duzelt', 'yenidenAc', 'ata'], ['reddet', 'yenidenAc', 'ata', 'reddet'], ]; for (const [ad, yollar] of [['mutlu yol', MUTLU_YOL], ['genis kume', GENIS_KUME]]) { const gorulen = kapsam(yollar); const eksik = TUM_GECISLER.filter((g) => !gorulen.has(g)); console.log(`${ad.padEnd(10)}: ${gorulen.size}/${TUM_GECISLER.length} gecis denendi`); if (eksik.length) console.log(` denenmeyen: ${eksik.join(', ')}`); }
mutlu yol : 4/10 gecis denendi denenmeyen: yeni+reddet, atandi+reddet, duzeltildi+yenidenAc, kapandi+yenidenAc, reddedildi+yenidenAc, yenidenAcildi+ata genis kume: 10/10 gecis denendi
Mutlu yol geçişlerin yüzde kırkını dener. Denenmeyen altı geçişin hepsi olağandışı akışlardır: reddetme, yeniden açma, doğrulamanın başarısız olması. Bunlar gerçek kullanımda en çok tartışma çıkaran akışlardır ve tam da bu yüzden en az sınananlardır.
Üç yol ekleyerek kapsam tamamlandı. Ölçüm, hangi yolların eklenmesi gerektiğini de söylüyordu — eksik geçişlerin listesi doğrudan yazılacak senaryoların listesidir.
Özet
- Hata insanın yaptığı yanlış, kusur onun üründe bıraktığı iz, arıza kusurun gözlenen dışa vurumudur; her kusur arızaya dönüşmez.
- Hata kaydının ölçütü, okuyan başka bir kişinin arızayı soru sormadan yeniden üretebilmesidir; gözlenen ve beklenen sonuç ayrı ayrı yazılır.
- Önem arızanın etkisini, öncelik düzeltmenin ne zaman yapılacağını söyler; ikisi bağımsızdır ve tek alana indirildiğinde kararlar karışır.
- Yaşam döngüsü bir durum makinesidir; örnekte kırk iki durum-olay çiftinden onu geçerli, otuz ikisi reddedilmesi gerekendir.
- Durum geçiş testinin ölçüsü geçiş kapsamıdır; mutlu yol on geçişten dördünü denedi, denenmeyenlerin listesi yazılacak senaryoların listesini verdi.
Kurs Kapanışı
Kurs iki soruyu yanıtladı. Birincisi testin ne olduğuydu. Kalite bir uygunluk yargısı olarak tanımlandı; ürün kalitesiyle süreç kalitesi ayrıldı; doğrulama ile geçerleme birbirinden koparıldı ve belirtimine tam uyan bir programın yine de yanlış olabileceği gösterildi. İşlevsel olmayan beklentiler adlandırılıp ölçülebilir ölçütlere çevrildi. Testin bir teknikler kümesi olmadan önce bir tutum olduğu, aynı koda bakan iki bakışın farklı şeyler bulduğu ölçüldü. Teslimat modelinin testi ucuzlatmadığı, yalnız aynı sınamanın ne zaman yapılacağını belirlediği gösterildi.
İkincisi hangi testin yazılacağıydı. Testler kapsadıkları sınıra, dayandıkları bilgiye ve amaçlarına göre sınıflandırıldı. Girdi seçimi sezgiden çıkarılıp denklik sınıfları, sınır değerleri ve karar tablosuyla gerekçelendirildi. Sınanacak şey bir senaryo biçimine kavuşturuldu ve o biçimden çalıştırılabilir teste geçildi. Beklenen sonucun nereden geldiği sorusu ayrı bir problem olarak ele alındı ve üç kâhin karşılaştırıldı. Son olarak bulunan sapmanın kayıttan kapanışa giden yolu bir durum makinesi olarak kuruldu.
Yanıtlanmayan soru testin nasıl yazılacağıdır. Bir denetimin iç düzeni nedir; hazırlık, uygulama ve doğrulama nasıl ayrılır? İddia ne söylemeli, ne söylememeli? Bir birim, dış dünyaya dokunmadan nasıl sınanır — bağımlılıklar sahte bağımlılıklarla nasıl değiştirilir ve hangi türü nerede kullanılır? Kapsam ölçümü ne söyler, hangi soruya yanıt vermez? Testi koddan önce yazmak tasarımı nasıl değiştirir?
Sonraki kurs, Birim Testi ve Test Odaklı Geliştirme, bu soruları ele alır. Hızlı ve bağımsız testlerin yazımı, iddiaların anlamlı kurulması, bağımlılıkların dışarıdan verilmesi, sahte bağımlılıkların doğru türde kullanılması, satır ve dal kapsamının okunması ve kırmızı–yeşil–yeniden düzenle döngüsü orada kurulur. Bu kursta seçilen testler, orada yazılacak testlerdir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.