← Nesim Apps

Davranışı Değiştirmeden Kotlin Multiplatform'a Geçiş: Eski Kodu Test Kâhini Olarak Tutmak

Nesim Razon, bağımsız Android geliştiricisi · 2026-11-16

#Geliştirici notları#Android

İki küçük RSS okuyucu uygulamayı ve bir uzay uygulamasını iOS'ta da çalışsın diye Kotlin Multiplatform'a taşıdım. Bu, Android ve Java sınıflarına yaslanan kodu yeniden yazmak demekti. Mevcut hiçbir kullanıcının bir fark görmesini istemedim; bu yüzden eski uygulamayı test kaynaklarında tuttum ve yenisiyle tartıştırdım. Süreç şöyle işledi.

Neyin yeniden yazılması gerekti

Ortak Kotlin kodu Android ya da Java sınıflarını çağıramaz, iOS'ta da bunların hiçbiri yoktur. RSS ayrıştırıcısı XmlPullParser, metin temizliği HtmlCompat kullanıyordu, tarihler SimpleDateFormat'tan geçiyordu ve uzay uygulaması günlük kartlarını java.util.Random ile karıştırıyordu. Dördü de iki platformda derlenen düz Kotlin'e dönüşmek zorundaydı.

Böyle bir yeniden yazım sessiz değişikliklerin klasik yeridir: aksanını kaybeden bir başlık, bir saat kayan bir tarih, farklı sırada gelen bir liste. Bunların hiçbiri çökme yaratmaz, bu yüzden bir kullanıcı yorum yazana kadar kimse fark etmez.

Eski kodu kâhin olarak tut

Kuralım basitti: eski uygulamayı silme, taşı. TurkSail'de önceki ayrıştırıcı, değiştirilmeden, Android test kaynaklarında LegacyRss adlı bir dosyaya gitti. Artık yayınlanmıyor ama hâlâ derleniyor ve güvendiğim cevapları üretiyor; çünkü gerçek kullanıcılar yıllardır onun çıktısına bakıyor.

Yeni ayrıştırıcı ortak modülde duruyor. Test aynı XML'i ikisine de verip sonuçların eşit olup olmadığını soruyor.

Eşitlik testi

Test bir beslemeyi yeni ve eski ayrıştırıcıyla çözüyor ve önce ikisinin de aynı sayıda yazı döndürdüğünü denetliyor. Sonra yazıları tek tek gezip altı şeyi alan alan karşılaştırıyor: başlık, bağlantı, tarih, kapak görseli, özet ve temizlenmiş HTML. Hata mesajı beslemeyi, yazı sırasını ve alanı söylüyor; böylece fark kolay bulunuyor.

Eski taraf için Android sınıfları gerektiği için test, uygulama modülünde Robolectric altında çalışıyor. Varsayılan olarak projeyle gelen örnek beslemeleri kullanıyor. -Drss.live=true ile başlatırsam gerçek sitelerin canlı beslemelerini de indirip onları karşılaştırıyor. Bu sistem özelliğinin modülün test yapılandırmasında test JVM'sine iletilmesi gerekiyor; yoksa anahtar sessizce hiçbir şey yapmıyor.

Kâhinin yakaladığı tuzaklar

Beslemelerdeki HTML'in kendisi XML'e kaçırılmış olduğundan varlıklar iki kez çözülüyor: bir kez XML geçişinde, bir kez de HTML geçişinde. Kaçırılmış bir ve işareti ile uuml içeren bir besleme, ancak iki geçişten sonra noktalı u harfine dönüşüyor. Bir kez çözen kısa yol yanlış metin veriyor.

HtmlCompat adlandırılmış HTML varlıklarını biliyordu, ortak kod bilmiyor. 96 Latin-1 adlandırılmış varlığın ve beslemelerin kullandığı birkaç diğerinin tablosunu yazdım; ondalık ve onaltılı sayısal varlıkları, temel aralığın dışındaki karakterler dahil, işliyorum. Bilinmeyen varlıklar olduğu gibi kalıyor; eski kod da böyle yapıyordu.

Tarihler ofsetli RFC 822 biçiminde: gün adı, gün, ay adı, saat ve artı ya da eksi dört haneli bir dilim. SimpleDateFormat gitti; bu yüzden bir düzenli ifadeyle ayrıştırıyor, ofseti elle kuruyor ve kotlinx-datetime ile çeviriyorum. Birim testleri hem ofsetli hem GMT'li tek bir somut zaman damgasını sabitliyor ve çöp girdinin eskisi gibi null döndürdüğünü denetliyor.

Değişmemesi gereken bir rastgele sayı üreteci

Uzay uygulaması günün kartını gösteriyor. Sıra, kart listesinin 2026 tohumlu java.util.Random ile karıştırılmasından geliyor ve kart sırası tarihten türetiliyor. Karıştırma değişseydi, mevcut her kullanıcı aynı gün birden farklı bir kart görürdü.

Bu yüzden java.util.Random'ı birebir yeniden üreten küçük bir sınıf yazdım: 48 bitlik doğrusal eşlenik üreteç, nextInt'in bir sınırın altında değer seçme yolu (yeniden deneme döngüsü ve ikinin kuvveti kısayolu dahil) ve Collections.shuffle'ın kullandığı aynı algoritma. Buradaki kâhin gerçeğin kendisi: bir test, listeyi JVM'de java.util.Random(2026) ile karıştırıyor ve kimliklerin benim sınıfımla aynı sırada çıktığını denetliyor.

Önemli çıkan küçük şeyler

Android indirilen beslemeyi, adresin özetiyle adlandırılan bir önbellek dosyasında tutuyor. Dosya adlarını aynı bıraktım; böylece güncellemeden sonra uygulama eski dosyayı hâlâ buluyor ve besleme çevrimdışı hemen açılıyor. Eşitlik testi değişmiş bir dosya adını yakalamazdı; bu yüzden burada yazıyorum.

Canlı karşılaştırma TurkSail için 7 kategoriyi ve 72 yazıyı, MedyaFobi için 20 yazıyı kapsadı; sıfır fark çıktı.

Bunun neyi kanıtlamadığı

Eşitlik testi yalnızca ona verdiğin girdileri kapsar. Örnek beslemeler ve bir canlı görüntü, gelecek hafta yayımlanacak, hiç görmediğim bir varlık ya da tarih biçimi içeren bir yazı hakkında hiçbir şey söylemez. Canlı beslemeler de değişir; bugünkü yeşil bir koşu bugün hakkında bir ifadedir.

Ayrıca doğruluğu değil eşitliği gösterir: eski kodda bir hata varsa yeni kod artık aynı hataya bilerek sahip. Hedef davranışı korumak olduğu için bunu kabul ettim; düzeltmeler ayrı ve görünür bir değişikliktir. Rastgele sayı üreteci için de aynı sınır geçerli: denediğim liste boyutları için eşleşiyor ve algoritmanın belirleyici olmasına güveniyorum.

Sık sorulan sorular

Eski kodu silmek yerine neden testlerde tutuyorsun?

Çünkü davranışın en güvenilir tarifi o. Yeni kodu onunla alan alan karşılaştırmak, beklenen değerleri elle yazmaktan hem hızlı hem daha güvenilir.

Eşitlik testi internet erişimi gerektirir mi?

Varsayılan olarak hayır. Projeyle gelen örnek beslemeleri kullanır. Canlı karşılaştırma yalnızca test -Drss.live=true ile başlatılınca çalışır.

Neden başka bir karıştırma kullanmak yerine java.util.Random'ı yeniden yazdın?

Farklı bir karıştırma, mevcut her kullanıcı için günün kartını değiştirirdi. Üreteci birebir yeniden üretmek diziyi aynı tutuyor.

Geçen bir eşitlik testi gerilemeyeceğini garanti eder mi?

Hayır. Yalnızca gördüğü girdileri kapsar ve canlı veri değişir; riski azaltır ama ortadan kaldırmaz.

Diğer yazılar