TestFly Part 14: Adım İzleri — Görsel Hata Ayıklama İçin Adım Başına ARIA/DOM Snapshot'ları
TestFly 1.1.0, trace'e adım başına snapshot ekliyor: her adım başladığı andaki erişilebilirlik ağacını (ya da budanmış DOM'u) kaydediyor, başarısız bir test son adımından sonraki sayfayı da bir diff ile yakalıyor ve artık Cucumber senaryoları da trace alıyor.
TestFly Part 14: Adım İzleri — Görsel Hata Ayıklama İçin Adım Başına ARIA/DOM Snapshot'ları
Özet: TestFly 1.1.0, trace'e adım başına snapshot ekliyor: artık her adım, başladığı andaki
erişilebilirlik ağacını (ya da dom/both ile sayfanın budanmış HTML'ini) kaydediyor, başarısız
bir test ise son adımından sonraki sayfayı da kaydedip öncesiyle bir diff gösteriyor. Yeni config
anahtarları tracing.snapshots, tracing.snapshotMaxKb ve tracing.snapshotOnFailureAfter neyin
yakalanacağını belirliyor, StepRecord yeni alanları taşıyor, TraceRecorder.saveIfEnabled trace'i
yazıyor — artık yalnızca TestNG ve JUnit 5'ten değil, Cucumber'dan da; HTML rapor da her testten
trace'e doğrudan bağlantı veriyor.
Adım İzi Nedir, Her Adımı Neden Snapshot'lıyoruz?
Bir trace, bir testin adım zaman çizelgesini yeniden oynatan tek bir bağımsız HTML dosyasıdır —
target/traces/{SınıfAdı}/{testMetodu}-trace.html. Adım isimleri, zaman damgaları, ekran görüntüleri,
hatanın anındaki son ekran görüntüsü, hata mesajı ve stack trace, kayıt açıksa video — hepsi burada.
Tek dosyayı herhangi bir tarayıcıda açarsın; sunucuya ihtiyaç yok.
Ekran görüntüleri sayfanın nasıl göründüğünü gösterir. DOM'da gerçekte ne olduğunu göstermez —
bir buton devre dışı mıydı, bir elementte aria-hidden var mıydı, beklediğin markup orada mıydı.
Adım başına snapshot'ların kapattığı boşluk tam da bu: her adımın ekran görüntüsünün yanında, trace
artık StepSnapshotCapture'ın adım başladığı anda gördüğü sayfanın metin snapshot'ını da gömüyor.
# testfly.yml
tracing:
enabled: trueTracing açıkken, her adım varsayılan olarak bir snapshot alır — teste ek kod gerekmez.
Her Adımın Snapshot'ı Ne İçeriyor?
Yakalama, adım başına bir kez çağrılan StepSnapshotCapture.captureForStep() tarafından yönetiliyor.
tracing.enabled'i kontrol ediyor, tracing.snapshots modunu okuyor ve yalnızca gerçekten canlı bir
driver oturumu varsa tarayıcıya dokunuyor — API ve veritabanı testleri, ya da tarayıcı oluşmadan önce
loglanan adımlar, hiç snapshot almıyor:
// StepSnapshotCapture.java (sadeleştirilmiş)
public static Snapshots captureForStep() {
TestFlyConfig.Tracing tracing = tracing();
if (tracing == null || !tracing.isEnabled()) {
return Snapshots.NONE;
}
String mode = tracing.getSnapshots();
boolean aria = mode.equals("aria") || mode.equals("both");
boolean dom = mode.equals("dom") || mode.equals("both");
WebDriver driver = liveDriver();
if (driver == null) {
return Snapshots.NONE;
}
int maxKb = tracing.getSnapshotMaxKb();
return new Snapshots(aria ? captureAria(driver, maxKb) : null,
dom ? captureDom(driver, maxKb) : null);
}Zaten bir adımın adını, zaman damgasını, durumunu ve ekran görüntüsünü tutan StepRecord, iki yeni
alan artı bir tane daha aldı; o da yalnızca başarısız bir test için sonradan doldurulan son durum:
// StepRecord.java
public StepRecord(String name, long offsetMs, String status, String screenshotBase64,
String ariaSnapshot, String domSnapshot) { ... }
public String getAriaSnapshot() { return ariaSnapshot; }
public String getDomSnapshot() { return domSnapshot; }
public String getAfterAriaSnapshot() { return afterAriaSnapshot; } // yalnızca başarısızlıktaaria, ariaSnapshot()'ın yazdırdığı aynı YAML metnini yakalıyor — küçük, okunabilir, CSS sınıfı
değişikliklerinden etkilenmiyor. dom, DomPruner tarafından script'lerden, stillerden ve
dekorasyondan budanmış sayfa HTML'ini yakalıyor; daha büyük ama erişilebilirlik ağacının göstermediği
markup'ı gösteriyor — örneğin hata ayıkladığın bir CSS sınıfını.
Neyin Yakalanacağını Nasıl Yapılandırırsın?
# testfly.yml
tracing:
enabled: true
snapshots: both # none | aria | dom | both — varsayılan: aria
snapshotMaxKb: 256 # adım başına tutulan en uzun snapshot, KB — varsayılan: 256
snapshotOnFailureAfter: true # başarısız testin son adımından sonraki sayfayı da yakalaTestFlyConfig, tracing.snapshots'ı erken doğrular — none, aria, dom veya both dışında
bir değer, çalışma sırasında değil config yüklenirken IllegalArgumentException fırlatır.
snapshotMaxKb'den uzun bir snapshot kesilir ve … truncated (N more characters) ile biter, böylece
tek bir ayrıntılı sayfa tüm trace dosyasını şişirmez.
Trace HTML'i Gerçekte Ne Gösteriyor?
Zaman çizelgesinde bir adıma tıkla, ekran görüntüsü paneli sekmelerle açılır: ARIA (before step),
DOM (before step), ve — yalnızca başarısız bir testin son adımında — ARIA (after failure) ve
hesaplanmış bir Diff sekmesi. Diff, öncesi ve sonrası YAML'ları arasında satır bazlı bir küme farkı;
trace'in kendi gömülü JavaScript'inde (TraceRecorder'daki lineDiff()) istemci tarafında hesaplanır:
yalnızca "öncesi"nde olan satırlar - ile, yalnızca "sonrası"nda olanlar + ile işaretlenir. Harici
bir diff kütüphanesi yok, ağ çağrısı yok — trace, e-postayla gönderebileceğin ya da Slack'e
bırakabileceğin tek bir dosya olarak kalıyor.
Zaman çizelgesindeki adım blokları da snapshot taşıdıklarında küçük bir 🌳 işareti alır, ekran görüntüsü için 📷 işaretinin yanında — hangi adımların tıklanacak bir şeye sahip olduğunu bir bakışta görürsün.
Bu Testlerimi Yavaşlatır ya da Başarısız Olma Riski Taşır Mı?
Bir snapshot almak adım başına bir script çağrısı — tipik bir sayfada birkaç milisaniye — ve yalnızca
tracing.enabled açıkken çalışır. StepSnapshotCapture, her yakalamayı başarısızlıkta hata fırlatmak
yerine null döndüren bir try/catch'e sarıyor: okunamayan bir sayfa, kapanmış bir driver ya da
kararsız bir script çağrısı testi asla başarısız kılmaz, yalnızca o adımı snapshot'sız bırakır.
TraceRecorder.saveIfEnabled'ın kendisi de aynı şekilde sarmalanmış:
// TraceRecorder.java
@TestFlyApi(since = "1.1.0")
public static void saveIfEnabled(String testId, String testName, boolean isPassing) {
try {
TestFlyConfig.Tracing tracing = TestFlyContext.getConfig().getTracing();
if (tracing == null || !tracing.isEnabled()) return;
if (isPassing && !tracing.isCaptureOnPass()) return;
save(testId, testName);
} catch (Exception ignored) {
}
}Bu Cucumber İle Çalışıyor Mu?
Evet — 1.1.0 itibarıyla CucumberHooks, TestNG ve JUnit 5'in kullandığı aynı trace mekanizmasını
çağırıyor, bu yüzden Gherkin senaryoları da snapshot'lar dahil bir trace dosyası alıyor; feature
dosyalarında ya da step definition'larda hiçbir değişiklik gerekmiyor. HTML test raporu da artık her
testin detay panelinden doğrudan kendi trace'ine bağlantı veriyor, yani raporda kırmızı bir satırdan,
neyin yanlış gittiğini gösteren tam adıma ve snapshot'a tek tıkla gidiyorsun.
SSS
Adım başına snapshot almak için testlerime kod eklemem gerekiyor mu?
Hayır. tracing.enabled: true ile, her adım tracing.snapshots'a göre (varsayılan aria) otomatik
olarak bir snapshot alır. Adım başına çağrılacak bir API yok.
Tarayıcısı olmayan bir testte, örneğin bir API testinde ne olur?
Hiçbir şey yakalanmaz. StepSnapshotCapture, snapshot almadan önce canlı bir WebDriver oturumu
olup olmadığını kontrol eder ve yoksa snapshot almadan geçer — yalnızca bir tane yakalamak için asla
bir driver oluşturmaz ya da yeniden oluşturmaz.
Bir snapshot yakalama hatası testimi başarısız kılabilir mi?
Hayır. Her yakalama yolu, bir istisnanın yutulup FINE seviyesinde loglandığı, asla yeniden
fırlatılmadığı şekilde sarmalanmıştır. Bir snapshot en iyi çaba ilkesiyle alınır; geçti/kaldı
sonucunu hiçbir zaman etkilemez.
Diff sekmesi erişilebilirlik ağacının gerçek bir yapısal farkı mı?
Hayır — "öncesi" ve "sonrası" ARIA YAML metni arasında, trace'in kendi gömülü JavaScript'inde hesaplanan satır bazlı bir küme farkıdır. Neyin ortaya çıktığını ya da kaybolduğunu görmek için yeterlidir, anlamsal bir ağaç farkı değildir.