Cypress ile Görsel Regresyon Testi: Pragmatik Bir Yaklaşım

Yayınlanan: 2022-03-11

Bileşen kitaplığımız Picasso'nun her yeni sürümü yayınlandığında, yeni özelliklerden en iyi şekilde yararlanmak için tüm ön uç uygulamalarımızı günceller ve tasarımlarımızı sitemizin tüm bölümleriyle uyumlu hale getiririz.

Geçen ay, yeteneğimizin iş bulmak ve müşterilerle etkileşim kurmak için kullandığı platform olan Toptal Talent Portal'da bir Picasso güncellemesi yayınladık. Sürümün büyük tasarım değişiklikleriyle geleceğini bilmek ve beklenmeyen sorunları en aza indirmek amacıyla, sorunları sürümden önce bulmamıza yardımcı olacak görsel regresyon testi tekniklerini kullanmak mantıklıydı.

Görsel regresyon testi yeni bir kavram değildir; Picasso'nun kendisi de dahil olmak üzere Toptal'daki diğer birçok proje zaten kullanıyor.

Percy, Happo ve Chromatic gibi araçlar, ekiplerin sağlıklı bir görsel regresyon ardışık düzeni oluşturmasına yardımcı olmak için kullanılabilir ve ilk başta bunları eklemeyi düşündük. Sonunda kurulum sürecinin çok zaman alacağına ve programımızı rayından çıkarabileceğine karar verdik. Geçişi başlatmak için kodu dondurmak için zaten bir tarihimiz vardı ve son teslim tarihine yalnızca birkaç gün kala yaratıcı olmaktan başka seçeneğimiz yoktu.

UI Testi ile Görsel Regresyon Testi

Projede görsel regresyon testlerimiz olmasa da Cypress kullanarak UI entegrasyon testlerini iyi bir şekilde kapsadık. Araç çoğunlukla bunun için kullanılmasa da, Cypress belgelerinde görsel teste ayrılmış bir sayfaya ve Cypress'i görsel test için yapılandırmaya yardımcı olacak tüm mevcut eklentileri listeleyen bir sayfaya sahiptir.

Selvi'den Ekran Görüntülerine

Mevcut belgeleri gözden geçirdikten sonra, cypress-snapshot-plugin'i denemeye karar verdik. Kurulumu sadece birkaç dakika sürdü ve kurduktan sonra, geleneksel bir görsel regresyon çıktısının peşinde olmadığımızı çabucak fark ettik.

Çoğu görsel regresyon aracı, anlık görüntüleri karşılaştırarak ve bilinen, kabul edilen bir temel ile bir sayfanın veya bileşenin değiştirilmiş sürümü arasındaki piksel farklılıklarını saptayarak istenmeyen değişiklikleri belirlemeye yardımcı olur. Piksel farkı, ayarlanan tolerans eşiğinden büyükse, sayfa veya bileşen manuel olarak incelenmek üzere işaretlenir. Ancak bu sürümde, UI bileşenlerinin çoğunda birkaç küçük değişiklik yapacağımızı biliyorduk, bu nedenle bir eşik belirlemek uygun değildi. Belirli bir bileşen %100 farklı olsa bile, yeni sürüm bağlamında yine de doğru olabilir. Benzer şekilde, birkaç piksel kadar küçük bir sapma, bir bileşenin şu anda üretim için uygun olmadığı anlamına gelebilir.

Test çalıştırmasının beklenen sonucunu ve fiili sonucunu gösteren ekran görüntüsü.
Şekil 1. Yanlış negatiflere yol açan küçük piksel farklılıkları örneği

Bu noktada, birbiriyle çelişen iki şey netlik kazandı: Piksel farklılıklarını fark etmek, sorunları belirlemeye yardımcı olmayacaktı ve tam da ihtiyacımız olan şey bileşenlerin yan yana karşılaştırılmasıydı. Anlık görüntü eklentisini bir kenara koyduk ve Picasso güncellemesi uygulanmadan önce ve sonra bileşenlerimizle bir görüntü koleksiyonu oluşturmaya başladık. Bu şekilde, yeni sürümlerin hala sitenin gereksinimlerine ve kitaplığın standartlarına uyup uymadığını belirlemek için tüm değişiklikleri hızla tarayabiliriz.

Yeni plan, bir bileşenin ekran görüntüsünü almak, yerel olarak depolamak, aynı bileşenin güncellenmiş Picasso sürümüyle dalda yeni bir ekran görüntüsünü almak ve ardından bunları tek bir görüntüde birleştirmekti. Sonuç olarak, bu yeni yaklaşım, başladığımızdan çok farklı değildi, ancak artık eklentiyi içe aktarmaya ve yeni komutlarını kullanmaya gerek duymadığımız için uygulama aşamasında bize daha fazla esneklik sağladı.

Görsel bir karşılaştırma akışını gösteren diyagram, görsel test çalıştırmasından sonra yeni ve eski sürümün resimlerinin nasıl birleştirildiğini.
Şekil 2. Görsel karşılaştırma akışı

Karşılaştırma Görüntüleri için API'lerden Yararlanma

Akılda net bir hedefle, Cypress'in ihtiyacımız olan ekran görüntülerini almamıza nasıl yardımcı olabileceğine bakmanın zamanı gelmişti. Belirtildiği gibi, Yetenek Portalı'nın çoğunu kapsayan çok sayıda UI testi yaptık, bu nedenle mümkün olduğunca çok sayıda kritik bileşen toplamak amacıyla her etkileşimden sonra ayrı öğelerin ekran görüntülerini almaya karar verdik.

Alternatif bir yaklaşım, test sırasında önemli anlarda tüm sayfanın ekran görüntülerini almak olabilir, ancak bu görüntüleri karşılaştırmanın çok zor olacağına karar verdik. Ayrıca, bu tür karşılaştırmalar, örneğin bir altbilginin değiştiğinin olmaması gibi insan hatasına daha yatkın olabilir.

Üçüncü bir seçenek, neyi yakalayacağınıza karar vermek için her bir test senaryosunu gözden geçirmek olurdu, ancak bu çok daha fazla zaman alacaktı, bu nedenle sayfalarda kullanılan tüm öğelere bağlı kalmak pratik bir uzlaşma gibi görünüyordu.

Görüntüleri oluşturmak için Cypress'in API'sine döndük. cy.screenshot() komutu, kutudan çıktığı haliyle bileşenlerin tek tek görüntülerini oluşturabilir ve After Screenshot API, dosyaları yeniden adlandırmamıza, dizinleri değiştirmemize ve görsel regresyon çalıştırmalarını standart olanlardan ayırt etmemize olanak tanır. İkisini birleştirerek, işlevsel testlerimizi etkilemeyen ve görüntüleri uygun klasörlerinde saklamamızı sağlayan çalıştırmalar oluşturduk.

İlk olarak, iki yeni çalıştırma türünü (temel ve karşılaştırma) desteklemek için eklenti dizinimizde index.js dosyasını genişlettik. Ardından, çalışma tipine göre resimlerimizin yolunu belirliyoruz:

 // plugins/index.js const fs = require('fs') const path = require('path') module.exports = (on, config) => { // Adding these values to your config object allows you to access them in your tests. config.env.baseline = process.env.BASELINE || false config.env.comparison = process.env.COMPARISON || false on('after:screenshot', details => { // We only want to modify the behavior of baseline and comparison runs. if (config.env.baseline || config.env.comparison) { // We keep track of the file name and number to make sure they are saved in the proper order and in their relevant folders. // An alternative would have been to look up the folder for the latest image, but this was the simpler approach. let lastScreenshotFile = '' let lastScreenshotNumber = 0 // We append the proper suffix number to the image, create the folder, and move the file. const createDirAndRename = filePath => { if (lastScreenshotFile === filePath) { lastScreenshotNumber++ } else { lastScreenshotNumber = 0 } lastScreenshotFile = filePath const newPath = filePath.replace( '.png', ` #${lastScreenshotNumber}.png` ) return new Promise((resolve, reject) => { fs.mkdir(path.dirname(newPath), { recursive: true }, mkdirErr => { if (mkdirErr) { return reject(mkdirErr) } fs.rename(details.path, newPath, renameErr => { if (renameErr) { return reject(renameErr) } resolve({ path: newPath }) }) }) }) } const screenshotPath = `visualComparison/${config.env.baseline ? 'baseline' : 'comparison'}` return createDirAndRename(details.path .replace('cypress/integration', screenshotPath) .replace('All Specs', screenshotPath) ) } }) return config }

Ardından, projenin package.json Cypress çağrısına karşılık gelen ortam değişkenini ekleyerek çalıştırmaların her birini çağırdık:

 "scripts": { "cypress:baseline": "BASELINE=true yarn cypress:open", "cypress:comparison": "COMPARISON=true yarn cypress:open" }

Yeni komutlarımızı çalıştırdığımızda, çalıştırma sırasında alınan tüm ekran görüntülerinin uygun klasörlere taşındığını görebildik.

Çalışma sırasında çekilen ve klasörlere taşınan görüntüleri gösteren ekran görüntüsü.
Şekil 3. Görsel çalıştırma sonuçları

Daha sonra, Cypress'in DOM öğelerini döndürmek için ana komutu olan cy.get() üzerine yazmaya ve varsayılan uygulamasıyla birlikte çağrılan herhangi bir öğenin ekran görüntüsünü almaya çalıştık. Ne yazık ki, orijinal komutu kendi tanımında çağırmak sonsuz bir döngüye yol cy.get() değiştirilmesi zor bir komuttur. Bu sınırlamaya geçici bir çözüm bulmak için önerilen yaklaşım, ayrı bir özel komut oluşturmak ve ardından öğeyi bulduktan sonra bu yeni komutun ekran görüntüsünü almasını sağlamaktır:

 Cypress.Commands.add("getAndScreenshot", (selector, options) => { // Note: You might need to tweak the command when getting multiple elements. return cy.get(selector).screenshot() }); it("get overwrite", () => { cy.visit("https://example.cypress.io/commands/actions"); cy.getAndScreenshot(".action-email") })

Ancak, sayfadaki öğelerle etkileşim çağrılarımız zaten dahili bir getElement() işlevine sarılmıştı. Bu yüzden tek yapmamız gereken, paketleyici çağrıldığında ekran görüntüsünün alındığından emin olmaktı.

Görsel Regresyon Testi Yoluyla Elde Edilen Sonuçlar

Ekran görüntülerini aldığımızda, geriye kalan tek şey onları birleştirmekti. Bunun için Canvas kullanarak basit bir düğüm betiği oluşturduk. Sonunda, komut dosyası 618 karşılaştırma görüntüsü oluşturmamızı sağladı! Yetenek Portalı'nı açarak bazı farklılıkları fark etmek kolaydı, ancak bazı sorunlar o kadar açık değildi.

Öğede kırmızı ve siyah renkleri gösteren Picasso'nun yanlış kullanımının öncesi ve sonrası örneği.
Şekil 4. Yeni Picasso yönergelerine uymama örneği; bir fark bekleniyordu, ancak yeni sürüm kırmızı bir arka plana ve beyaz bir metne sahip olmalıydı

"Sonra" resminde bir onay kutusunun yanında yanlış hizalanmış metni gösteren, hafif bozuk bileşen düzeni örneği öncesi ve sonrası.
Şekil 5. Biraz bozuk bileşen yerleşimi örneği

UI Testine Değer Katmak

Her şeyden önce, eklenen görsel regresyon testleri yararlı olduğunu kanıtladı ve onlarsız gözden kaçırmış olabileceğimiz birkaç sorunu ortaya çıkardı. Bileşenlerimizde farklılıklar beklememize rağmen, gerçekte neyin değiştiğini bilmek sorunlu vakaları daraltmaya yardımcı oldu. Bu nedenle, projenizin bir arayüzü varsa ancak henüz bu testleri yapmıyorsanız, başlayın!

Buradaki ikinci ders ve belki de daha önemlisi, mükemmelin iyinin düşmanı olduğunu bir kez daha hatırlamış olmamızdır. Önceden kurulum olmadığı için bu sürüm için görsel regresyon testleri çalıştırma olasılığını dışlamış olsaydık, geçiş sırasında birkaç hatayı kaçırmış olabiliriz. Bunun yerine, ideal olmasa da uygulanması hızlı olan bir plan üzerinde anlaştık, bunun için çalıştık ve işe yaradı.

Projenize sağlam bir görsel regresyon hattı uygulamakla ilgili daha fazla ayrıntı için lütfen Cypress'in görsel test sayfasına bakın, ihtiyaçlarınıza en uygun aracı seçin ve eğitim videolarını izleyin.