Tüm Yazılar
KategoriTools
Okuma Süresi
8 dk
Yayın Tarihi
2026-10-15
Kelime Sayısı
1.295kelime

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

Robotlar, kendini onaran aramalar ve test edilmiş kurallarla okunur XCUITest: iOS-Testing-Automation-Framework 2.0

Özet

2.0 UI test mantığını neden test edilmiş saf Swift'te tutuyor, kütüphanesi Swift Testing'i neden import etmiyor; bir parola XCTest log'una nasıl sızdı.

  • XCUITest'in ham API'siyle yazılan bir UI testi kısa sürede app.buttons["signIn.submit"].tap() çağrılarından oluşan bir listeye dönüşür.
  • Kurallar saf Swift'te, XCUITest ince bir katman.
  • Paketi uygulama target'ına değil UI test target'ına ekleyin, çünkü XCTest'e link ediliyor.
  • Kaynak kod: [github.com/muhittincamdali/iOS-Testing-Automation-Framework](https://github.com/muhittincamdali/iOS-Testing-Automation-Framework)
Robotlar, kendini onaran aramalar ve test edilmiş kurallarla okunur XCUITest: iOS-Testing-Automation-Framework 2.0

İçindekiler


Problem

XCUITest'in ham API'siyle yazılan bir UI testi kısa sürede app.buttons["signIn.submit"].tap() çağrılarından oluşan bir listeye dönüşür. Böyle bir test, element henüz dokunulabilir değilken dokunur, biri accessibility identifier'ı değiştirdiğinde kırılır ve akışın hangi adımında bozulduğunu söylemeyen bir mesajla başarısız olur. Screenshot'lar gözle karşılaştırılır, erişilebilirlik elle kontrol edilir ve tek rapor CI log'udur.

iOS-Testing-Automation-Framework, test target'larına eklenen ve bu boşlukları dolduran bir Swift paketi. UI testlerinde robotlar (ekran başına bir sınıf), identifier değişse de çalışan aramalar, beş kurallı bir erişilebilirlik denetimi, fark görselli screenshot karşılaştırması ve junit.xml ile bir Markdown job özeti ekler. Her test target'ında SwiftUI snapshot'ları, polling ile bekleme, retry, spy'lar ve seed'li test verisi sunar. Hiçbir bağımlılığı yok.

15 Ekim 2026'da yayınlanan 2.0.0 bir yeniden kurulum. Repo Ağustos 2025'te açıldı ve Şubat 2026'da, büyük kısmı README'de duran bir özellik listesiyle 1.0.0 olarak etiketlendi. Haziran 2026'da kaynaklar yer tutucu kodla değiştirildi: hiçbir şey karşılaştırmayan bir görsel regresyon motoru, yalnız ekrana yazan bir erişilebilirlik denetçisi ve artık derlenmeyen bir test target'ı. CHANGELOG bunu 2.0.0 bölümünün ilk paragrafında açıkça yazıyor. Yeniden kurulum, kardeş repo iOSTestingTools'un işe yarayan kısımlarını da bu pakete taşıdı; o repo artık arşivlendi ve buraya yönlendiriyor.

Tasarım kararları ve alternatifler

Kurallar saf Swift'te, XCUITest ince bir katman. Healing, denetim, görüntü karşılaştırma, raporlar ve secret maskeleme düz değerler üzerinde çalışıyor: ElementDescriptor (tür, identifier, label, value, placeholder, frame, derinlik) ve Bitmap. XCUI klasörü yalnız ekranın tek bir snapshot'ını descriptor'lara çeviriyor ve hataları XCTest üzerinden bildiriyor. Bu sayede swift test, simülatöre gerek kalmadan macOS'ta 118 test koşabiliyor. Alternatif, mantığı doğrudan XCUIElement extension'larına yazmaktı: daha az kod, ama yalnız yavaş uçtan uca UI testleriyle doğrulanabilir. Haziran'daki yer tutucu kod, bu yolun hiçbir şey test edilmediğinde neye benzediğini gösteriyordu: sonuçlar print'e gidiyordu.

Kütüphane modülünde import Testing yok. Robotlar hataları XCTFail ile bildiriyor, çünkü XCUITest zaten XCTestCase içinde koşuyor. Unit testler için olan assertSnapshot(of:) bir issue kaydetmek yerine SnapshotError fırlatıyor; fırlatılan hata hem Swift Testing'de hem XCTest'te testi aynı şekilde kırıyor. UI katmanının ilk sürümü tersini yapıyordu; neden değişmesi gerektiği bir sonraki bölümde.

Tahmin yürütmeyen healing. LocatorHealer sırayla şunları deniyor: tam identifier, locator'ın label'ını taşıyan tek element, placeholder'ı taşıyan tek metin alanı ve en benzer identifier (büyük/küçük harf ve noktalama yok sayılır, varsayılan en düşük benzerlik 0.8). Bir strateji eşit derecede iyi iki aday bulursa .ambiguous döner ve test XCTest'in olağan mesajıyla kırılır. Yanlış elemente dokunan bir test, kırılan bir testten daha kötüdür. İyileşen her arama HealingLog'a ve Markdown özetine somut bir düzeltme olarak yazılıyor, örneğin Update the locator "loginButton" to "signIn.submit". Alternatifler, her zaman en iyi adayı seçmek (yanlış geçen test riski) ya da hiç healing yapmamaktı.

Kendi piksel karşılaştırıcısı ve diskte duran baseline'lar. ImageComparator 8-bit RGBA bitmap'ler üzerinde çalışıyor: kanal başına tolerans, izin verilen değişmiş piksel oranı, yok sayılan bölgeler (bir saat, bir tarih) ve değişen her pikseli maviye boyayan bir fark görseli. İlk koşu baseline'ı kaydeder ve başarısız olur; eksik bir baseline asla sessizce geçmez. swift-snapshot-testing gibi bir kütüphane daha olgun, ama bağımlılıksız olma hedefini bozardı; perceptual tolerans bunun yerine yol haritasında. Repodan gelen girdiler sınırlanıyor: snapshot adları güvenli dosya adlarına çevriliyor, bir PNG'nin boyutu decode edilmeden önce header'dan okunup 50 milyon piksel sınırıyla karşılaştırılıyor.

Her testten sonra yeniden yazılan raporlar. TestResultCollector XCTest'i gözlemliyor ve junit.xml ile summary.md'yi en sonda bir kez değil, her testten sonra yeniden yazıyor. Xcode bir runner'ı erken sonlandırıp yenisini başlattığında, sonda yazılan bir raporun üzerine ikinci process yalnız kendi sonuçlarını yazabiliyor ve hatalı bir koşu "Passed" görünüyor. Yeniden kurulum bunu 17394ce commit'inde düzeltti; aynı commit paralel koşular için perProcessFileNames seçeneğini de ekledi.

Ne ters gitti: maskelenen ama yine de log'a ulaşan bir parola

Yeniden kurulumdan iki gerçek hata, paketin neden kendi soyutlamalarına karşı değil gerçek çıktıya karşı test edildiğini gösteriyor.

Birincisi derlemenin kendisiyle ilgiliydi. İlk XCUITest katmanı (39d945c), çalışma zamanında Swift Testing'in aktif olup olmadığına bakıp Issue.record ya da XCTFail çağırıyordu. Gerçek bir UI test bundle'ına eklendiğinde cannot load module 'Testing' as 'Testing' hatasıyla derlenmedi. Xcode UI test bundle'larını -module-alias Testing=_Testing_Unavailable ile derliyor; bu yüzden Swift Testing'i içeride bile import eden bir modül orada kullanılamıyor. bed5f99 commit'i import'u kaldırdı ve unit test assertion'ını fırlatan bir yapıya taşıdı. Paketin kendi unit testleri bunu asla gösteremezdi; gösteren, gerçek bir tüketici target'ı oldu.

İkincisi bir güvenlik hatasıydı. Robot.typeSecret(_:into:) parolayı redactor'a kaydediyordu; böylece paketin adım adlarında, hata mesajlarında ve raporlarında •••• olarak maskeleniyordu ve dokümantasyon da bunu söylüyordu. Ama parola hâlâ XCUIElement.typeText ile giriliyordu ve XCTest bu çağrının activity'sini yazılan metinle adlandırıyor: Type 'correct-horse-batt...' into "signIn.password" SecureTextField. Bu başlık xcodebuild log'una ve result bundle'a yazılıyor; hiçbir redactor oraya ulaşamıyor. Mevcut test yalnız ortak redactor'ın parolayı maskeleyip maskelemediğine bakıyordu; log'un kendisine bakan yoktu.

d7f7c1d commit'i secret'ın giriliş biçimini değiştirdi. typeSecret artık iOS ve macOS'ta secret'ı cihazdan çıkmayan bir pasteboard öğesine koyuyor ve ⌘V'ye basıyor; XCTest bunu bir modifier'lı tuş basışı olarak log'luyor. Pasteboard'un dolu olup olmadığı hiçbir metni okumayan ve paste izni sormayan hasStrings/types ile kontrol ediliyor; alanın değeri değişince pasteboard boşaltılıyor. iOS simülatöründe uygulama açıldıktan sonraki ilk ⌘V sık sık düştüğü için yapıştırma, alan 1.5 saniye sonra hâlâ değişmemişse en fazla üç kez tekrarlanıyor; hiç değişmeyen bir alan, adımı secret'ı içermeyen bir mesajla kırıyor.

dc57725 commit'i bu garantiyi doğrulanabilir hâle getirdi. Örnek uygulamanın workflow'u artık bir result bundle yazıyor; scripts/check-secrets-in-results.sh her testin activity'lerini ve hata ayrıntılarını xcresulttool ile, junit.xml ve summary.md ile birlikte okuyor ve bir test parolası tam hâliyle ya da ilk 12 karakteriyle görünürse job'ı kırıyor (XCTest uzun yazılan metni kısaltıyor). Script, düzeltmeden önce kaydedilmiş bir result bundle'da üç testte sızıntı buldu; yapıştırmalı üç koşuda hiçbir şey bulmadı. Ders şu: bir garantiyi, korumayı vaat ettiği artefaktın içinde arayarak test et.

Aynı tur, benzer türden daha küçük sızıntılar da buldu: ASCII dışı harf içeren bir secret URL-kodlandığında maskelenmiyordu (904d159), tırnak içindeki bir credential değeri yalnız ilk boşluğa kadar maskeleniyordu (43ffcca) ve healing önerileri summary.md'ye maskelenmeden ulaşıyordu (db54ad3).

Kullanım

Paketi uygulama target'ına değil UI test target'ına ekleyin, çünkü XCTest'e link ediliyor. ContentView'unda counter.label identifier'lı bir Text ve counter.button identifier'lı bir buton olan bir uygulamada README'deki quick start testi şöyle:

swift
1import XCTest
2import iOSTestingAutomationFramework
3 
4final class CounterRobot: Robot {
5 var label: XCUIElement { app.staticTexts["counter.label"] }
6 var button: XCUIElement { app.buttons["counter.button"] }
7}
8 
9final class CounterUITests: XCTestCase {
10 @MainActor
11 func testTappingCounts() {
12 let app = XCUIApplication()
13 app.launch()
14 
15 let counter = app.on(CounterRobot.self)
16 counter
17 .tap(counter.button)
18 .tap(counter.button)
19 .assertLabel(of: counter.label, equals: "Tapped 2 times")
20 }
21}

Her dokunuş ve label kontrolü test raporunda ayrı bir adım olarak görünüyor. Aynı test, CI'da AutomationShowcase örnek uygulamasında QuickStartTests olarak, quick start ekranını açan launch argümanlarıyla koşuyor.

Bir Locator, testi bir yeniden tasarımdan sonra da çalışır tutuyor:

swift
1// "loginButton" was the identifier before the redesign; the app now uses "signIn.submit".
2let button = app.element(Locator("loginButton", label: "Sign In", kind: .button), timeout: 1)
3XCTAssertTrue(button.exists)
4XCTAssertEqual(button.identifier, "signIn.submit")

junit.xml ve summary.md için collector'ı TestResultCollector.install(reportName:) ile bir kez kurun ve xcodebuild test çalıştırırken TEST_RUNNER_TESTING_AUTOMATION_REPORT_DIR değişkenini verin.

Sınırlar

  • XCUITest katmanı, tek bir örnek uygulamadaki 9 UI testiyle CI'ın iPhone simülatöründe doğrulandı. Fiziksel cihazlarda, başka iOS sürümlerinde ya da başka uygulamalarda denenmedi.
  • En düşük sürümler iOS 17 ve macOS 14, Swift 6.2 araçlarıyla. tvOS, watchOS ve visionOS desteklenmiyor; 1.0.0 iOS 15, watchOS 8 ve tvOS 15 beyan ediyordu.
  • Görüntü karşılaştırması yalnız kanal başına tolerans kullanıyor. Bir makinede kaydedilen baseline başka bir makinede farklı çıkabilir; perceptual tolerans ile metin ve JSON snapshot stratejileri yol haritasında.
  • Healing bir heuristik. İyileşen bir arama testin geçmesine izin veriyor ve CI'ı kırmıyor; önerileri birinin okuması gerekiyor.
  • TestResultCollector bir XCTestObservation; Swift Testing ile yazılmış testler junit.xml'de yer almıyor.
  • typeSecret pasteboard'u boşaltıyor ama önceki içeriği geri yüklemiyor, çünkü onu okumak bir paste izni sorusu gösterirdi.
  • Sertleştirme paketin kendi testlerine dayanıyor; dışarıdan bir inceleme yapılmadı. Paket henüz Swift Package Index'te değil, ancak .spi.yml repoda hazır.

Kaynak kod: github.com/muhittincamdali/iOS-Testing-Automation-Framework

Etiketler

#swift#xcuitest#testing#accessibility#ci
Muhittin Çamdalı

Muhittin Çamdalı

Senior Mobile Engineer

12+ yıllık deneyime sahip Senior Mobile Engineer. iOS, Flutter ve React Native ile mobil ürünler geliştiriyorum; full-stack ve AI-native çalışıyorum.

iOS Geliştirme Haberleri

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

Onay e-postasındaki bağlantıyı açıp “Aboneliğimi onayla” düğmesine bastığında aboneliğin başlar. Bültende açılma/tıklama istatistikleri tutulur; dilediğin an tek tıkla ayrılabilirsin. Gizlilik

Paylaş

İlgili İçerik