Ana içeriğe geç
%50 indirim tüm planlarda, sınırlı süreyle. Başlangıç fiyatı $2.48/mo
13 min left
Geliştirici Araçları ve DevOps

CI hattınızda görsel regresyon testlerini kendiniz nasıl barındırırsınız

S Yazan Sajjad 13 dk okuma
Self-hosted visual regression testing running in a CI pipeline with BackstopJS and Docker

Görsel regresyon testleriniz dizüstünüzde geçiyor. Push ediyorsunuz, hat çalışıyor ve aynı testler ya tarayıcıyı sessizce çökertiyor ya da aslında yalnızca kenar yumuşatma farkı olan elli tane "değişiklik" işaretliyor. Kodunuzda hiçbir şey değişmedi ama CI aynı fikirde değil.

"Yerelde çalışıyor" ile "CI'da çalışıyor" arasındaki bu boşluk neredeyse her zaman bir altyapı sorunudur, test sorunu değil. Bir CI hattında görsel regresyon testlerini güvenilir biçimde kendiniz barındırmak için doğru şekilde birbirine bağlanmış dört parçaya ihtiyacınız var: Docker executor'lı bir CI runner'ı, headless Chromium'un paylaşımlı belleği tüketmeyeceği şekilde yapılandırılmış bir container, CI'ın okuyabileceği raporlar üretecek şekilde kurulmuş bir karşılaştırma aracı ve headless tarayıcıların gerçekte tükettiği belleğe göre boyutlandırılmış bir VPS.

Bu rehber o yığını baştan sona kuruyor. Sonunda elinizde çalışan bir .gitlab-ci.yml (ya da Gitea karşılığı), Chromium'un çökmesini engelleyen bir Docker ortamı, hattınızın okuyabileceği JUnit raporları üreten BackstopJS ve tüm bunları çalıştıracak doğru boyutlandırılmış bir VPS olacak.

Özetle

  • Tarayıcıyı yerelde ve CI'da aynı Docker imajında çalıştırın. Yanlış pozitif farkların çoğu ortamlar arasındaki font ve render farklarından geliyor. İmajı sabitleyin, hepsi kaybolsun.
  • 64MB'lık /dev/shm varsayılanını düzeltin. Docker'ın varsayılan paylaşılan bellek boyutu Chromium'un renderer'ını aç bırakır ve o da CI'da sessizce çöker. docker-compose.yml içinde shm_size: '2gb' ayarlayın ya da Chromium'a --disable-dev-shm-usage geçirin.
  • Kendi barındırabileceğiniz birincil araç olarak BackstopJS'i kullanın. MIT lisanslı (v6.3.25), bir --docker bayrağıyla Docker öncelikli ve JUnit raporları üretiyor. Zaten Playwright kullanıyorsanız, yerleşik toHaveScreenshot() kurulum gerektirmeyen gayet iyi bir başlangıç noktası.
  • Temel olarak 4GB RAM'e göre boyutlandırın. Bellek bütçenizi cömert tutun. Pratik CI çalıştırmalarında bir Chromium ekran görüntüsü işi, tam sayfa yakalamalar sırasında boştayken ihtiyaç duyar göründüğünden çok daha fazla bellek tüketebiliyor; bu yüzden bir ila iki ardışık ya da hafif paralel iş için 4GB RAM daha güvenli bir temel.

Bu Kılavuzun Kapsamadıkları

Bu, ayağa kaldırmaya zaten karar verdiğiniz bir altyapı için kurulum rehberi. Birkaç konu bilerek kapsam dışında:

  • Kapsamlı bir araç karşılaştırması değil. Bu rehber, kendi barındırdığınız bir CI ortamında gerçekten işletebileceğiniz yığına odaklanıyor: BackstopJS, Docker ve kendi runner'ınız.
  • Argos'u kendiniz barındırmayı ele almıyor. Argos açık kaynak, ancak kamuya açık ürün akışı ve dokümantasyonu basit bir üretim ortamı barındırma yolundan çok, barındırılan Argos uygulamasına ve CI entegrasyonlarına odaklanıyor. Bu rehberin amacı açısından BackstopJS daha güvenli bir örnek.
  • Lost Pixel'i önermiyor. O proje Nisan 2026'da arşivlendi; yeni kurulumlar için BackstopJS kullanın.
  • Kendi barındırma ile yönetilen SaaS tartışmasını yeniden açmıyor. Buradaysanız, Percy veya Chromatic'i zaten elediniz demektir.

Ön Koşullar

İlk komuttan önce birkaç şeyin hazır olması gerekiyor:

  • Kendi barındırdığınız bir CI runner'ı ya da bir tane kurma planı (aşağıda anlatılıyor).
  • Runner sunucusunda kurulu Docker.
  • Mevcut bir test hedefi olan bir Node.js projesi (çalışan bir uygulama URL'si ya da yakalanacak bir dizi bileşen rotası).
  • Runner'ın bulunduğu VPS veya sunucuya shell erişimi.

Görsel testler neden yerelde geçip CI'da bozuluyor

macOS'ta referansınızı render eden tarayıcı, bir Linux CI container'ında karşılaştırmayı render eden tarayıcı değil. Playwright'ın kendi dokümantasyonu bu konuda net: tarayıcı render'ı; sunucunun OS'una, sürümüne, ayarlarına, donanımına, güç kaynağına, headless moduna ve başka etkenlere göre değişebiliyor. Önerisi de aynı ölçüde doğrudan: tutarlı ekran görüntüleri için testleri referans ekran görüntülerinin üretildiği ortamda çalıştırın.

Bu tek öneri, kurulumun geri kalanının var olma sebebi. Neredeyse her sahte hatanın arkasında üç uyumsuzluk var:

  • Font ve kenar yumuşatma farkları. Geliştirme makineniz ve CI container'ı farklı font paketleri ve farklı alt piksel render'ı taşıyor. Metin ağırlıklı sayfalar hiçbir şey değişmese bile her çalıştırmada fark veriyor.
  • Headless ve arayüzlü render farkı. Arayüzlü bir tarayıcı ile headless olanı aynı sayfayı biraz farklı yerleştirebiliyor.
  • Paylaşımlı belleğin tükenmesi. Sessiz olan bu. Docker'ın varsayılan /dev/shm boyutu 64MB ve headless Chromium renderer süreçleri için paylaşımlı bellek kullanıyor. Bellek bitince Chromium işe yarar bir hata vermeden çöküyor. İşiniz öylece ölüyor ya da askıda kalıyor.

İlk ikisi, hem referans üretimi hem de karşılaştırma için tek bir Docker imajını sabitleyerek çözülüyor. Üçüncüsü aşağıdaki Docker yapılandırma bölümünde çözülüyor. Üçünü de düzeltin, "CI'da kararsız" olan testler deterministik hale gelsin.

Three reasons visual regression tests pass locally but fail in CI: font and anti-aliasing differences between the dev machine and the CI container, headless versus headed rendering differences, and shared-memory exhaustion from Docker's 64MB /dev/shm default crashing headless Chromium

Aracınızı seçmek: BackstopJS mi, Playwright'ın yerleşik karşılaştırması mı

Uçtan uca testler için zaten Playwright kullanıyorsanız en hızlı yol yerleşik toHaveScreenshot() doğrulaması: sıfır ek bağımlılık, piksel düzeyinde karşılaştırma ve kutudan çıkar çıkmaz platform ekli referanslar. Küçük bir süit için gayet meşru bir durak; ortak bileşen kütüphanesi olmayan ve kabaca elli ekranın altındaki ekipler için ihtiyacınız olan tek şey olabilir.

Yolun bittiği yer inceleme iş akışı. Playwright'ın karşılaştırması yalnızca piksel düzeyinde ve PR inceleme arayüzü yok; dolayısıyla meşru bir görsel değişiklik yığınını onaylamak, anlık görüntüleri elle yeniden üretmek demek. Referans sayınız arttıkça bu manuel onay döngüsü can sıkıcı hale geliyor.

BackstopJS tam olarak bu döngü için yapılmış özel araç. MIT lisanslı (şu an v6.3.25), bir --docker bayrağıyla Docker öncelikli ve bir CI runner'ının doğrudan okuyabildiği JUnit XML üretiyor. Referans iş akışı (bir referans üret, ona karşı test et, kabul edilen farkları yeni referans olarak onayla), kendi barındırılan görsel regresyon kurulumlarının çoğunun eninde sonunda istediği operasyon modeli. Rehberin geri kalanında işlenen örnek BackstopJS.

Başka yerlerde önerildiğini görebileceğiniz iki araç bu anlatımın dışında: Argos burada ele alınmıyor çünkü kamuya açık ürün akışı barındırılan uygulamaya ve CI entegrasyonlarına odaklı; Lost Pixel ise arşivlenmiş durumda. Pratik ve kendi barındırdığınız bir kurulum için daha güvenli örnek BackstopJS.

EksenBackstopJSPlaywright toHaveScreenshot()
PR inceleme iş akışıYerleşik rapor ve approve komutuAnlık görüntülerin elle yeniden üretilmesi
Referans yönetimiReference / test / approve komutlarıTest başına anlık görüntü dosyaları
Docker ile normalize edilmiş render--docker bayrağıKendi imajını getir
Kurulum yüküAyrı bağımlılıkPlaywright kullanıyorsanız zaten var

Daha fazla araç ve ölçüt üzerinden daha derin bir kıyaslama için, BackstopJS, Argos ve Lost Pixel karşılaştırmamız seçim kararının tamamını ele alıyor.

VPS üzerinde kendi CI runner'ınızı kurmak

Render tutarlılığını asıl sağlayan parça Docker executor'ı: runner her işi sizin tanımladığınız bir container içinde çalıştırır, yani tarayıcı ortamı her seferinde aynıdır. Runner platformunu, ondan başka ne kadar iş beklediğinize göre seçin.

Kendi barındırdığınız GitLab CE ağır seçenek: depolar, CI/CD, registry, issue'lar ve merge request'ler tek bir örnekte. Bu rehber açısından önemli olan kısım GitLab Runner'ın Docker executor'ı, bu sayede her görsel regresyon işi her seferinde aynı sabitlenmiş container imajının içinde çalışıyor. GitLab, Gitea, Jenkins, Forgejo, Portainer ve Docker'ın hepsi tek tıkla dağıtım olarak şurada mevcut: Cloudzy pazar yeri, bu da kurulumu epeyce kısaltıyor.

act-runner ile Gitea ikilisi hafif alternatif. Gitea, GitHub Actions uyumlu bir iş akışı motoruna (act-runner üzerinden) sahip Go tabanlı bir Git servisi; ekibiniz Actions söz dizimine alışkınsa ayağa kaldırması küçük ve hızlı bir runner.

Hangisini seçerseniz seçin yapılandırma hedefi aynı: iş kabul eden ve bunları bir Docker executor'ıyla çalıştıran bir runner. Sonraki her şey (container yapılandırması, BackstopJS, örnek hat) o executor'ın hazır olduğunu varsayıyor.

Chromium'un çökmemesi için Docker'ı yapılandırmak

İnsanlara en çok zaman kaybettiren hata bu. Docker /dev/shm'i varsayılan olarak 64MB ile bağlıyor. Headless Chromium bu paylaşımlı bellek bölümünü renderer süreçleri için kullanıyor ve tam sayfa bir ekran görüntüsü sırasında 64MB'tan çok daha fazlasına ihtiyaç duyuyor. Bellek bitince renderer ölüyor, üstelik çoğu zaman paylaşımlı belleği işaret eden hiçbir hata mesajı olmadan. İş askıda kalıyor, zaman aşımına uğruyor ya da genel bir tarayıcı çökmesi bildiriyor.

İki çözüm var ve ikisi de işe yarıyor.

Çözüm A: paylaşımlı bellek boyutunu Compose içinde yükseltin. shm_size anahtarı, konteynerin /dev/shm bölümünün boyutunu belirler. Docker Compose dosya referansı shm_size'ın, servis container'ına izin verilen paylaşımlı bellek boyutunu yapılandırdığını doğruluyor. Servis bloğunda ayarlayın:

# docker-compose.yml
services:
  vrt:
    image: backstopjs/backstopjs:6.3.25
    shm_size: '2gb'          # override the 64MB default
    volumes:
      - ./:/src
    working_dir: /src

Çözüm B: Chromium'a /dev/shm'i hiç kullanmamasını söyleyin. --disable-dev-shm-usage bayrağı, Chromium'un paylaşımlı bellek dosyalarını paylaşımlı bellek bölümü yerine /tmp altına yazmasını sağlıyor. Bunu tarayıcı başlatma argümanlarınızda geçirin. backstop.json yapılandırmasında bunun yeri engineOptions; Playwright'ta ise launchOptions:

// backstop.json (fragment)
{
  "engine": "puppeteer",
  "engineOptions": {
    "args": ["--disable-dev-shm-usage", "--no-sandbox"]
  }
}

Compose dosyası sizin kontrolünüzdeyse Çözüm A daha temiz; yalnızca tarayıcı başlatma bayraklarına dokunabiliyorsanız taşınabilir seçenek Çözüm B. İkisini birden uygulamanın da zararı yok.

Profesyonel İpucu: Yerelde ve CI'da tam olarak aynı Docker imajını kullanın. BackstopJS bunu --docker bayrağıyla bedavaya veriyor: yakalamayı sabitlenmiş bir referans imajının içinde çalıştırıyor, böylece makineniz ile runner aynı şekilde render ediyor. Font ve kenar yumuşatma kaynaklı yanlış pozitifleri ortadan kaldıran şey bu: imajı bir kez sabitleyin ve hayalet farkların peşinde koşmayı bırakın.

Two fixes for headless Chromium crashing in Docker CI: raising the container's shared memory with shm_size set to 2gb in docker-compose.yml, or passing the --disable-dev-shm-usage flag so Chromium writes shared-memory files to /tmp instead of the 64MB /dev/shm partition

BackstopJS'i CI için kurmak

BackstopJS, CI runner'ınızın derlemeyi geçirmek ya da başarısız saymak için okuduğu bir JUnit XML raporu üretiyor; onu elle çalıştırmak yerine bir hatta bağlamanın bütün amacı zaten bu. Kurun ve bir yapılandırma başlatın:

npm install --save-dev backstopjs
npx backstop init

Ardından backstop.json'ı yakalamak istediğiniz sayfalara yönlendirin ve CI raporunu açın:

// backstop.json (fragment)
{
  "id": "my_app_vrt",
  "viewports": [
    { "label": "desktop", "width": 1440, "height": 900 }
  ],
  "scenarios": [
    {
      "label": "Homepage",
      "url": "http://app:3000/",
      "selectors": ["document"]
    }
  ],
  "report": ["CI"],
  "engine": "puppeteer",
  "engineOptions": {
    "args": ["--disable-dev-shm-usage", "--no-sandbox"]
  }
}

JUnit XML'i üreten şey CI rapor ayarı. Bu komutları kendi makinenizden yerelde çalıştırırken --docker bayrağını kullanın. Zaten backstopjs/backstopjs imajını kullanan bir CI işinin içindeyse doğrudan backstop test çalıştırın. Referans iş akışı üç komuttan ibaret:

npx backstop reference --docker   # capture the approved baseline
npx backstop test --docker        # run the comparison, emit the report
npx backstop approve              # accept the current diffs as the new baseline

Referans yönetimiyle ilgili bir uyarı: her meşru UI değişikliği, yeni ekran görüntülerini onaylamak için approve çalıştırmak demek. Kabaca yüz senaryoyu geçtikten sonra bu onay adımı gerçek bir operasyon maliyeti: birinin farkları gözden geçirip hangilerinin kasıtlı olduğuna karar vermesi gerekiyor ve yük, aracı benimseyen ekiple birlikte büyüyor. Ölçekli görsel regresyon testinin asıl bedeli bu bakım işi; büyük bir süite bağlanmadan önce bunu bütçelemekte fayda var.

Çalışan bir CI hattı yapılandırması

Hat mantıksal olarak iki parçada çalışıyor: referansı bir kez (ya da talep üzerine) üret, sonra her değişiklikte ona karşı karşılaştır. Aşağıdaki örnek, Docker executor'ı kullanan bir GitLab CI yapılandırması; paylaşımlı bellek düzeltmesi backstop.json içindeki başlatma bayraklarıyla halihazırda uygulanmış durumda.

# .gitlab-ci.yml
stages:
  - visual-regression

visual_regression:
  stage: visual-regression
  image:
    name: backstopjs/backstopjs:6.3.25
    entrypoint: [""]
  script:
    - backstop test
  artifacts:
    when: always
    reports:
      junit: backstop_data/ci_report/xunit.xml
    paths:
      - backstop_data/bitmaps_test
      - backstop_data/html_report
    expire_in: 1 week

junit satırı raporu GitLab'e teslim ediyor, böylece hatalar merge request arayüzünde görünüyor; paths satırı ise fark bitmap'lerini saklıyor, böylece gerçekte neyin değiştiğine bakabiliyorsunuz. act-runner'lı Gitea'da aynı adımlar Actions tarzı bir iş akışına şöyle oturuyor:

# .gitea/workflows/visual-regression.yaml
name: Visual Regression
on: [push, pull_request]

jobs:
  vrt:
    runs-on: ubuntu-latest
    container:
      image: backstopjs/backstopjs:6.3.25
      options: --shm-size=2gb          # raise /dev/shm for the job container
    steps:
      - uses: actions/checkout@v4
      - run: backstop test

Gitea iş akışındaki --shm-size=2gb container seçeneğine dikkat edin. Bu, Çözüm A ile aynı paylaşımlı bellek düzeltmesi; düzenlenecek ayrı bir Compose dosyasının olmadığı yerde iş container'ı seviyesinde uygulanıyor.

VPS'inizi headless Chromium için boyutlandırmak

Buradaki kısıt CPU değil, RAM. Headless Chromium'un CPU kullanımı ani sıçramalar halinde (yakalama sırasında zirve yapıyor, aralarda boşta duruyor), bu yüzden mütevazı bir çekirdek sayısı rahatça yetişiyor. Aynı anda kaç iş çalıştırabileceğinizi belirleyen tavan bellek. Bir headless Chromium süreci boştayken kabaca 300–500MB civarında duruyor ve tam sayfa ekran görüntüsü alırken 1–2GB'a tırmanıyor; yani boyutlandırmayı belirleyen şey eşzamanlı iş başına RAM.

RAMUygun olduğu senaryo
2GBDar minimum: tek bir ardışık iş
4GBÖnerilen temel: 1–2 eşzamanlı iş
8GB3–4 eşzamanlı iş
16GBBüyük süitler ve paralel hatlar

Bu rakamlar bir üretici kıyaslaması değil, gerçek CI çalıştırmalarından çıkarılmış saha rehberliği; dolayısıyla onları yaklaşık başlangıç noktaları olarak görün ve kendi tepe bellek kullanımınızı izleyin.

Bölümün özeti: güvenilir temel 4GB RAM; her ek eşzamanlı görsel regresyon işi için kabaca 2GB pay ekleyin.

Kendi sunucunuzda VRT çalıştırmanın püf noktası şu: belleği yetersiz kalan bir runner gürültülü biçimde hata vermez, Chromium'u sessizce çökertir; yani tam da bu kurulumun önlemek için var olduğu belirtiyi üretir. Yeterli pay bırakmak, satın alabileceğiniz en ucuz sigortadır. Bir runner sunucusunu sıfırdan kurmak istemiyorsanız, Cloudzy'nin pazar yerinde şunlar için tek tıkla kurulum var: kendi sunucunuzda barındırılan GitLab ve kendi sunucunuzda barındırılan Gitea için tek tıkla dağıtımlar sunuyor; birkaç dakikada çalışan bir CI örneğine kavuşuyorsunuz, üstelik süitinizin ihtiyaç duyduğu headless Chromium payına göre tam isabetli boyutlandırabileceğiniz bir VPS üzerinde.

VPS memory sizing guide for headless Chromium visual regression jobs: 2GB for a single sequential job, 4GB as the recommended baseline for one to two concurrent jobs, 8GB for three to four concurrent jobs, and 16GB for large suites and parallel pipelines

Sıkça Sorulan Sorular

Bir CI/CD hattında görsel regresyon testini nasıl kurarım?

BackstopJS'i kurun, backstop.json'ı hedef sayfalarınıza yönlendirin ve JUnit XML üretmesi için CI rapor seçeneğini ayarlayın. Kendi makinenizden çalıştırırken backstop reference --docker ve backstop test --docker kullanın, böylece ekran görüntüleri sabitlenmiş Docker imajının içinde render edilir. CI'da işi backstopjs/backstopjs imajının içinde çalıştırıp doğrudan backstop test çağırın, ardından JUnit raporunu backstop_data/ci_report/xunit.xml konumundan yayınlayın. Docker executor'lı, kendi barındırdığınız bir runner kullanın ve headless Chromium için en az 4GB RAM'e göre boyutlandırın.

Headless Chromium bir CI container'ında ne kadar RAM ister?

Bir headless Chromium süreci boştayken kabaca 300–500MB kullanıyor ve tam sayfa ekran görüntüsü alırken 1–2GB'a sıçrıyor. Güvenilir bir CI için bir ila iki eşzamanlı iş başına yaklaşık 4GB RAM planlayın ve her ek paralel iş için kabaca 2GB ekleyin. CPU kısıt değil, sıçramalı bir kullanım gösteriyor; aynı anda kaç iş çalıştırabileceğinizi belirleyen şey RAM.

Görsel regresyon testlerim neden yerelde geçip CI'da başarısız oluyor?

Tarayıcı render'ı ortamdan ortama değişiyor: fontlar, kenar yumuşatma, headless ya da arayüzlü mod ve OS çıktıyı değiştiriyor. Çözüm, referans üretimi ile karşılaştırmayı aynı Docker imajında çalıştırmak, böylece ikisi de aynı şekilde render ediliyor. BackstopJS'in --docker bayrağı, yakalamaları sabitlenmiş bir referans imajının içinde çalıştırarak bunu yapıyor.

Chromium Docker CI'daki /dev/shm hatasını nasıl düzeltirim?

Docker'ın varsayılan /dev/shm boyutu 64MB; bu headless Chromium'un renderer'ı için fazla küçük ve sessiz çökmelere yol açıyor. Ya docker-compose.yml içinde shm_size: '2gb' ile (veya iş container'ında --shm-size=2gb ile) boyutu yükseltin ya da Chromium'un başlatma bayraklarına --disable-dev-shm-usage ekleyin, böylece paylaşımlı bellek dosyalarını /tmp altına yazsın.

Playwright toHaveScreenshot mı kullanmalıyım, özel bir görsel regresyon aracı mı?

Playwright'ın yerleşik toHaveScreenshot() işlevi küçük süitler için yeterli: ortak bileşen kütüphanesi olmayan, PR inceleme arayüzüne ihtiyaç duymayan ve kabaca elli ekranın altındaki durumlar. Düzgün bir referans onay iş akışına, bir inceleme raporuna ve Docker ile normalize edilmiş render'a ihtiyacınız olduğunda BackstopJS gibi özel bir araca geçin; referans sayınız büyüdükçe bu önem kazanıyor.

Lost Pixel hâlâ bakımda mı?

Hayır. Lost Pixel Nisan 2026'da arşivlendi ve yeni kurulumlar için aday değil. Kendi barındırdığınız bir görsel regresyon testi kurulumu için BackstopJS kullanın.

Paylaş

Bloga göz at

Okumaya devam et.

Dağıtmaya hazır mısın? 2,48 $/ay'dan başlayan fiyatlarla.

2008'den beri bağımsız bulut. AMD EPYC, NVMe, 40 Gbps. 14 gün para iade garantisi.