Free

Testleri Claude yazsın, sen yalnızca review et: 1.562 satırlık pratik

TopicReview'un 1.562 satırlık testinin neredeyse tamamını Claude yazdı. Ben yalnızca review yapıyorum — iki haftadan uzun süredir canlıda, sonraki tüm commit'ler test ekliyor, hiçbiri yeniden yazmıyor. Bu yazı testlerin neden devretmek için en ideal iş olduğunu, review'da neye bakılıp neye bakılmadığını (gerçek spec'te eksik kalan bir edge case dahil) ve bu bölüşümün varsayılan yapılandırmasını anlatıyor.


Önceki yazı TopicReview için "119 spec yeşil" ile kapandı. Okuyucunun asıl sorması gereken bir sonraki soru: o testleri kim yazdı?

Cevap: Claude 1.562 satır test kodunun neredeyse tamamını yazdı. Ben yalnızca review ettim. İki haftayı aşkın süredir canlıda, bu 1.562 satırın bakım örüntüsü yalnızca yeni test eklemek, eskileri asla yeniden yazmamak şeklinde.

Bu yazı testlerin Claude'a devredilecek en iyi iş olmasının nedenini, review'da nelere bakılıp nelerin yok sayılacağını ve bu bölüşümün pratikte nereye kadar gittiğini anlatıyor.

Önce rakamları ortaya dökelim

TopicReview'un testleri 7 dosyaya yayılmış durumda:

spec/services/topic_review_service_spec.rb   760 satır (88 test)
spec/requests/topic_reviews_spec.rb          281 satır (32 test)
spec/requests/review_appeals_spec.rb         152 satır (16 test)
spec/requests/review_votes_spec.rb           127 satır
spec/policies/topic_review_policy_spec.rb    109 satır
spec/jobs/close_topic_review_job_spec.rb      71 satır (7 test)
spec/models/topic_review_spec.rb              62 satır
───────────────────────────────────────────
                                          1.562 satır

Dört tür testi kapsar: service (iş mantığı), request (controller + entegrasyon), policy (Pundit yetkilendirmesi), job (zamanlanmış görevler).

İlk commit d162f1e Co-Authored-By: Claude Sonnet 4.6 etiketini taşıyor ve bu satırların 1.100'den fazlasını tek seferde getiriyor. Sonraki tüm spec ilgili commit'ler "Add test for..." biçiminde—tek bir refactor veya rewrite yok:

00393fc Add test for finalize! with zero votes (expired review)
3f53304 Add test for finalize! with legacy votes missing reasoning
3b185da Update specs to use PROVISIONAL_PENALTY constant

Boşluk yamıyor, yeniden yapmıyor. Bu ayrıntı ilerde önemli.

Testler neden devretmek için en iyi iş

Dört sert sebep:

1. Girdi ve çıktılar açık. Testin özü "verilen state → beklenen davranış"tır. Bu Claude'un en iyi olduğu çeviri işidir—spec'i iddiaya dönüştürmek. İş kodunun bazen ödünleşmeye gereksinimi olur; testlerin neredeyse hiç yoktur.

2. Mekanik × yüksek hacim. Tek bir describe .open! "uygun juri var / yok / topic yok / zaten aktif review var" dört context'i kapsamak zorunda; her birinde 2–5 it. İnsan üçüncü context'te köşe kapatmaya başlar. Claude 88. it'i birinciyle aynı özenle yazar.

3. Aşırı kısa geri bildirim döngüsü. Bir test yazarsın, rspec'i çalıştırırsın, saniyeler içinde geçip geçmediğini öğrenirsin. İş kodu sorunların yüzeye çıkması için günlerce gerçek kullanım ister. Kısa döngü = Claude'un hatası rspec tarafından yerinde yakalanır—sen gözetlemek zorunda kalmazsın.

4. Doğal olarak paralel. it blokları bağımsızdır, gizli eşleşme yoktur, önemsizce ölçeklenir. Bir seferde onlarca izole test üretmek tam da Claude'un iyi olduğu şeydir.

Review'da neye bakılır, neyi görmezden gelirsin

Bütün bölüşümün ekseni burası.

Görmezden gel:

  • RSpec söz dizimi doğru mu → Claude neredeyse hiç şaşmaz
  • Mock kalitesi → bariz over-mock olmadıkça iyidir
  • Factory estetiği → önemli değil, çalışıyorsa çalışır
  • Stil tutarlılığı → bir şey bozuksa Claude'a bir talimatla toplu düzelttir

Bak:

  • Edge case'ler gerçekten kapsandı mı
  • Test adları gerçek beklenen davranışı anlatıyor mu
  • Olması gereken ama yazılmamış bir test var mı

Sonuncusu review'un gerçek değeridir. Claude "aklına gelen" testleri kapsar ama aklına gelmeyen testler kendiliğinden yazılmaz. İnsan review'u tam da buraya oturur—iş kurallarından geriye doğru çalışarak eksik kapsamayı bulmak.

Somut örnek: review'un gerçekten yakaladığı şey

spec/services/topic_review_service_spec.rb başındaki describe ".open!"u açalım:

describe ".open!" do
  context "when there are eligible jurors" do
    # review durumu doğru / post under_review / assignments oluşturuldu / author bildirildi / jurors bildirildi / çift open olmuyor
  end
  context "when there are no eligible jurors" do
    # review oluşur ama assignments olmaz
  end
  context "when post has no topic" do
    # nil döndürür
  end
end

Kapsamlı görünüyor. Ama model'deki gerçek eligible_jurors kuralı üç grubu dışlıyor:

def eligible_jurors
  excluded_ids = [ post.user_id ] + post.reports.pluck(:user_id) + review_votes.where(stage: :initial).pluck(:user_id)
  User.jurors_and_judges.where.not(id: excluded_ids.uniq)
end

Şimdi testlere bakın—"post yazarı asla juri olarak seçilmez" iddiasını hangi test yapıyor?

service_spec.rb ve model_spec.rb'yi tarayın: yok. model_spec.rb yalnızca pending_vote_by scope'unun birkaç vakasını test ediyor; eligible_jurors'ı doğrudan kapsamıyor. service_spec.rb'de sadece bir yorum var: # Jurors must NOT be the post author—bu setup'ta bir yorum, iddia değil.

Review'un yakalayabileceği şey işte bu: üç dışlama kuralı (yazar / raporlayan / ilk turda oy kullanan), hiçbiri bir testle korunmuyor. Sonra biri eligible_jurors'u refaktör edip yanlışlıkla post.user_id'yi dışlama listesinden düşürürse, mevcut tüm testler geçer—ve production sessizce yazarların kendi jürilerinde oturmasına izin verir.

Claude yanılmadı—kendisinden istenen şeyi test etti. Yalnızca kendiliğinden şunu sormadı: "bu üç kuralın her biri test koruması gerektiriyor mu?" Bu soru—kurallardan kapsamaya geriye gitmek—review'un işidir.

(Dürüst olalım: ilk review'da bunu ben de kaçırdım. Bu yazıyı yazarken yapılan ikinci denetimde fark ettim. Yani review de tek seferlik değil—ama yine de review etmemekten 10× iyi.)

Sonraki commit'ler bu bölüşümün pratik gerçekliğini kanıtlıyor

"Claude yazar + insan review eder" kusursuz olsaydı, ilk commit'ten sonra yeni test commit'i olmazdı. Gerçek daha ilgi çekici—boşluk yaması var ama yeniden yazma yok:

00393fc Add test for finalize! with zero votes (expired review)
3f53304 Add test for finalize! with legacy votes missing reasoning

İlki hata sonrası regression test—e8cb2db Default to keep verdict when review expires with zero votes fix, 00393fc eş test. İkincisi aynı örüntüyle abaa22e Fix CloseTopicReviewJob failing due to reasoning validation on old votes'ı izler.

Bu iki commit aynı anda iki şeyi kanıtlıyor:

  • Review %100 vakayı yakalamadı—bu yüzden production iki hatayı açığa çıkardı
  • Ama test mimarisi yeterince dayanıklıydı; yeniden yapılandırma olmadan test eklemeye devam edebildik—bu yüzden commit'ler "Add test for..." ve "Rewrite ... spec" değil

"Yeterince iyi + yamayla sürdürülebilir" "kusursuz"dan çok daha gerçekçi bir çıta. Kusursuz review peşinde koşmak, Claude'a test devretmenizi engelleyen şey. 'Yeterince iyi'yi kabul etmek bu bölüşümü çalıştıran şey.

Claude'a tam olarak bırakılmaması gereken testler

Her test tam devire uygun değil:

  • E2E happy-path—ürün merceği gerektirir. Claude yazabilir ama "teknik olarak tamamlanır"ı kapsama eğilimindedir, "kullanıcının gerçekten takıldığı yer"i kaçırır
  • Güvenlik testleri—saldırgan zihniyeti gerektirir. Claude muhafazakardır, standart olmayan saldırı yüzeylerini (SQL anahtar sözcük enjeksiyonu, aşırı uzun string, alternatif unicode) kaçırır
  • Performans baseline'ları—gerçek dağıtım ortamının sayılarını gerektirir. Claude eşik değerlerini rastgele atar
  • Büyük ölçekli fixture / factory yeniden yapılandırması—bu mimari seviyede değişiklik; review'un yakalayacağı şey değil, plan mode'a geri dön

Bu durumlarda insan önderlik eder, Claude yardımcı olur.

Varsayılan yapılandırma

Bu bölüşümü çalıştırılabilir bir default'a çevirmek için:

  1. Özellik başlamadan önce, ben iş kurallarını anlatırım (RSpec sözleşmelerini değil)
  2. Claude hem uygulamayı hem testleri yazar
  3. Testleri çalıştırırız. Geçer = devam. Kalır = Claude kendi düzeltir
  4. Ben review ederim:
    • Söz dizimi / mock / factory'ye bakmam
    • Kapsamaya bakarım: her iş kuralı en az bir testle korunuyor mu?
    • Edge case'leri sorguya çekerim: "0 satır / null / eşzamanlılık / yetki ihlali"—teker teker
    • Test adlarını okurum—ad bana neyin test edildiğini söylemiyorsa Claude'a yeniden adlandırtırım
  5. Production'da bulunan hatalar regression test olarak geri döner—bu bölüşümün normal aşınması, başarısızlık değil

Kapanış

Programcının test okurken harcadığı zihinsel kaynak kod okurken harcadığından azdır. Testler tekrarlı, mekanik, yorucu ve gerekli. Bunların tümü Claude'un güçlü bölgesini tarif ediyor—sıkılmaz, yorulmaz, 50. it'te köşe kapatmaz.

Senin işin "test yazmak" değil—"her iş kuralının bir testle kapsandığından emin olmak". Biri uygulama, diğeri yargı. Yargı sende kalır; uygulama Claude'a gider.

119 spec / 1.562 satırın tek commit'te çıkıp iki haftadan fazla rework'süz hayatta kalması—daha iyi test yazdığım için değil, hiç test yazmadığım için gerçekleşti. Claude'un yapmadığı tek bir şeyi yapıyorum: hangi iş kurallarının korunmaya değer olduğuna karar veriyorum.