Takip et

Kaynak Sızıntılarını Durdurun: Playwright Testlerinde AbortSignal Nasıl Kullanılır?

Kaynak Sızıntılarını Durdurun: Playwright Testlerinde AbortSignal Nasıl Kullanılır? Playwright testlerinde asenkron işlemlerden kaynaklanan kaynak …

Kaynak Sızıntılarını Durdurun: Playwright Testlerinde AbortSignal Nasıl Kullanılır?

Playwright testlerinde asenkron işlemlerden kaynaklanan kaynak sızıntıları, testlerin yavaşlamasına ve güvenilmezliğe yol açabilir. Ağ istekleri veya zamanlayıcılar gibi uzun süreli operasyonların etkin yönetimi kritik öneme sahiptir. Bu makale, AbortSignal‘ı Playwright testlerinizde nasıl kullanacağınızı detaylandıracaktır. AbortSignal ile asenkron operasyonları kontrol altına alarak kaynak sızıntılarını önleyecek ve testlerinizin daha verimli çalışmasını sağlayacağız.

1. Asenkron Operasyonlar ve Kaynak Sızıntıları Nedir?

Playwright testleri, modern web uygulamalarının karmaşık asenkron davranışlarını taklit eder. Ancak bu asenkron yapı, doğru yönetilmediğinde test ortamında istenmeyen yan etkilere yol açabilir.

1.1. Asenkron Operasyonların Testlerdeki Rolü

Web uygulamaları genellikle ağ istekleri (API çağrıları), animasyonlar, zamanlayıcılar (setTimeout, setInterval) ve kullanıcı etkileşimleri gibi asenkron işlemlerle doludur. Playwright, bu işlemleri taklit eder ve test eder. Örneğin, bir butona tıklandığında yapılan bir API çağrısının yanıtını beklemek veya belirli bir elementin görünmesini beklemek asenkron operasyonlardır. Bu tür işlemlerin doğası gereği tamamlanması belirli bir süre alabilir ve testin akışını etkileyebilir.

1.2. Kaynak Sızıntıları ve Test Güvenilirliği

Kaynak sızıntısı, bir testin tamamlanmasına rağmen bazı asenkron işlemlerin hala arka planda çalışmaya devam etmesi veya temizlenmemesi durumudur. En yaygın kaynak sızıntıları şunlardır:

  • Tamamlanmamış Ağ İstekleri: Testin sonlanmasına rağmen hala yanıt bekleyen HTTP istekleri.
  • Aktif Zamanlayıcılar: Testin bitiminden sonra bile çalışmaya devam eden setTimeout veya setInterval çağrıları.
  • Bellek Sızıntıları: Gereksiz yere tutulan bellek referansları.

Bu sızıntılar, testlerin tutarsız (flaky) olmasına, birbirini etkilemesine, daha yavaş çalışmasına ve hata ayıklamayı zorlaştırmasına neden olur. Özellikle uzun süreli veya çok sayıda test senaryosu çalıştırıldığında bu sorunlar daha belirgin hale gelir.

1.3. Playwright Testlerinde Ortaya Çıkan Sorunlar

Playwright, varsayılan olarak birçok işlemi otomatik olarak temizlese de, özel olarak tanımlanmış asenkron işlemler veya üçüncü taraf kütüphaneler aracılığıyla başlatılan operasyonlar testin ömrünü aşabilir. Bu durumlar, testlerin beklenmedik şekilde başarısız olmasına veya bir sonraki testin önceki testten kalan kaynaklarla kirlenmesine yol açabilir.

2. AbortSignal’a Genel Bakış

AbortSignal ve AbortController, web platformunun standart bir parçasıdır ve asenkron operasyonları iptal etmek için tasarlanmıştır.

2.1. AbortController ve AbortSignal Nedir?

AbortController, bir veya daha fazla asenkron işlemi iptal etmek için kullanılan bir DOM API’sidir. AbortController‘ın bir signal özelliği vardır, bu özellik bir AbortSignal nesnesi döndürür. Bu AbortSignal nesnesi, iptal edilebilir bir operasyona iletilir. AbortController.abort() metodu çağrıldığında, ilişkili tüm AbortSignal nesneleri bir abort olayı tetikler. Bu olay, operasyonun iptal edilmesi gerektiğini belirtir.

2.2. Nasıl Çalışır?

Temel iş akışı şöyledir:

  1. Bir AbortController örneği oluşturulur.
  2. controller.signal nesnesi, iptal edilebilir asenkron operasyona (örneğin, fetch isteği veya setTimeout) parametre olarak iletilir.
  3. Operasyonun iptal edilmesi gerektiğinde, controller.abort() metodu çağrılır.
  4. AbortSignal‘ı dinleyen operasyonlar, abort olayını yakalar ve kendilerini temizleyerek veya sonlandırarak yanıt verir.

Bu mekanizma, özellikle uzun süreli veya potansiyel olarak takılı kalabilecek işlemleri güvenli bir şekilde sonlandırmak için çok etkilidir.

2.3. Yaygın Kullanım Alanları

AbortSignal, modern JavaScript uygulamalarında yaygın olarak kullanılır. En bilinen örnekler şunlardır:

  • fetch API: Ağ isteklerini iptal etmek için fetch(url, { signal: controller.signal }).
  • setTimeout / setInterval: Özel zamanlayıcıları temizlemek için signal.addEventListener('abort', () => clearTimeout(timerId)).
  • Web Sockets: Bağlantıları kapatmak için.
  • Diğer Asenkron API’ler: Birçok yeni ve modern web API’si AbortSignal desteği ile gelmektedir.

Bu standart API’nin varlığı, Playwright testlerinde de tutarlı ve güçlü bir iptal mekanizması sağlamamıza olanak tanır.

3. Playwright Testlerinde AbortSignal Neden Önemli?

Playwright testlerinde AbortSignal kullanımı, testlerin daha kararlı, hızlı ve güvenilir olmasını sağlayan önemli avantajlar sunar.

3.1. Uzun Süreli Ağ İsteklerini Yönetme

Testler sırasında, özellikle yavaş yanıt veren veya hiç yanıt vermeyen harici API’lere yapılan istekler, testin takılı kalmasına veya zaman aşımına uğramasına neden olabilir. AbortSignal kullanarak bu tür istekleri belirli bir süre sonra veya testin sonlanması durumunda proaktif olarak iptal edebiliriz. Bu, testlerin gereksiz yere beklemesini önler ve zaman aşımı hatalarının daha kontrollü bir şekilde yönetilmesini sağlar.

3.2. Test Hatalarında veya Zaman Aşımlarında Temizleme

Bir test başarısız olduğunda veya Playwright’ın genel zaman aşımına uğradığında, arka planda çalışan asenkron işlemlerin otomatik olarak temizlenmesi garantilenmez. AbortSignal ile, testin yaşam döngüsüne bağlı bir sinyal oluşturabiliriz. Testin sonlandığı (başarılı veya başarısız) veya zaman aşımına uğradığı anda bu sinyali tetikleyerek tüm ilişkili operasyonların temizlenmesini sağlayabiliriz. Bu, test ortamının bir sonraki test için temiz kalmasını garanti eder.

3.3. Test Güvenilirliğini ve Performansını Artırma

Kaynak sızıntıları, testlerin “flaky” (aralıklı olarak başarısız olan) olmasına neden olur. AbortSignal kullanarak bu sızıntıları önlemek, testlerin daha tutarlı sonuçlar vermesini sağlar. Ayrıca, gereksiz yere bekleyen işlemlerin iptal edilmesi, testlerin toplam yürütme süresini kısaltarak test paketinin daha hızlı tamamlanmasına yardımcı olur. Daha hızlı testler, daha sık geri bildirim ve daha verimli geliştirme süreçleri anlamına gelir.

4. Playwright ile AbortSignal Kullanımı: Temel Senaryolar

AbortSignal‘ı Playwright testlerinize entegre etmek, testlerinizi daha sağlam hale getirecektir. İşte temel kullanım senaryoları.

4.1. Ağ İsteklerini İptal Etme

Playwright’ın page.waitForRequest veya page.waitForResponse gibi metotları doğrudan AbortSignal‘ı desteklemez. Ancak, Playwright’ın kendisi zaman aşımlarını yönetir. AbortSignal‘ı, test içindeki özel fetch çağrıları gibi manuel olarak başlattığınız ağ isteklerini iptal etmek için kullanabilirsiniz.

import { test, expect } from '@playwright/test';

test('AbortSignal ile fetch isteğini iptal etme', async ({ page }) => {
  const controller = new AbortController();
  const signal = controller.signal;

  // Belirli bir süre sonra isteği iptal et
  const timeoutId = setTimeout(() => {
    console.log('İstek zaman aşımına uğradı, iptal ediliyor...');
    controller.abort();
  }, 2000); // 2 saniye sonra iptal et

  try {
    // Örnek bir uzun süreli API isteği simülasyonu
    const responsePromise = fetch('https://httpbin.org/delay/5', { signal }); // 5 saniye gecikmeli bir istek
    const response = await responsePromise;
    const data = await response.json();
    console.log('İstek başarıyla tamamlandı:', data);
    clearTimeout(timeoutId); // Başarılı olursa zamanlayıcıyı temizle
  } catch (error: any) {
    if (error.name === 'AbortError') {
      console.log('İstek AbortSignal tarafından iptal edildi.');
    } else {
      console.error('İstek sırasında bir hata oluştu:', error);
    }
    clearTimeout(timeoutId); // Hata durumunda da zamanlayıcıyı temizle
  }
});


Bu örnekte, fetch isteği 5 saniye sürmesi gerekirken, AbortSignal sayesinde 2 saniye sonra iptal edilmektedir.

4.2. Özel Zamanlayıcıları Temizleme

Testlerinizde setTimeout veya setInterval kullanarak özel gecikmeler veya periyodik işlemler tanımlayabilirsiniz. Bu zamanlayıcıların test sonunda temizlenmesi önemlidir.

import { test, expect } from '@playwright/test';

test('AbortSignal ile setTimeout temizleme', async ({ page }) => {
  const controller = new AbortController();
  const signal = controller.signal;

  let timerExecuted = false;
  const timer = setTimeout(() => {
    timerExecuted = true;
    console.log('Zamanlayıcı çalıştı!');
  }, 5000); // 5 saniye sonra çalışacak

  // AbortSignal tetiklendiğinde zamanlayıcıyı temizle
  signal.addEventListener('abort', () => {
    clearTimeout(timer);
    console.log('Zamanlayıcı AbortSignal tarafından temizlendi.');
  });

  // Testin ortasında veya belirli bir koşulda iptal et
  await page.goto('https://www.example.com');
  // ... diğer test adımları ...

  // Örneğin, 2 saniye sonra zamanlayıcıyı iptal et
  await page.waitForTimeout(2000);
  controller.abort();

  // Testin sonunda zamanlayıcının çalışmadığını kontrol edebiliriz
  expect(timerExecuted).toBe(false);
});


Bu sayede, testin beklenenden erken bitmesi veya bir hata oluşması durumunda, gereksiz zamanlayıcıların arka planda çalışmaya devam etmesi engellenir.

4.3. Playwright'ın test.use ve test.setTimeout ile Entegrasyon

Playwright'ın test.use hook'u, test bağlamını özelleştirmek için harika bir yoldur. Bir AbortController'ı test bağlamına ekleyerek, her test için otomatik olarak bir signal sağlayabiliriz.

import { test as base, expect } from '@playwright/test';

// Kendi özel test fixture'ımızı tanımlıyoruz
const test = base.extend<{ abortController: AbortController, abortSignal: AbortSignal }>({
  abortController: async ({}, use) => {
    const controller = new AbortController();
    await use(controller);
    // Test bittiğinde veya hata oluştuğunda sinyali tetikle
    controller.abort();
  },
  abortSignal: async ({ abortController }, use) => {
    await use(abortController.signal);
  }
});

test('AbortSignal ile test özel zamanlayıcısı', async ({ page, abortSignal }) => {
  let longRunningOperationCompleted = false;

  // Bir asenkron operasyon başlat
  const operationPromise = new Promise((resolve, reject) => {
    const timeoutId = setTimeout(() => {
      longRunningOperationCompleted = true;
      console.log('Uzun süreli operasyon tamamlandı.');
      resolve();
    }, 5000); // 5 saniye sürecek

    abortSignal.addEventListener('abort', () => {
      clearTimeout(timeoutId);
      console.log('Uzun süreli operasyon AbortSignal tarafından iptal edildi.');
      reject(new Error('Operation aborted')); // Operasyonu reddet
    });
  });

  await page.goto('https://www.example.com');

  // Testin varsayılan zaman aşımı 30 saniye olabilir, ama biz operasyonu 2 saniye sonra iptal edeceğiz.
  // Bu durumda, testin kendisi başarılı olacak ama operasyon iptal edilecek.
  await page.waitForTimeout(2000); // Sadece 2 saniye bekle

  // Operasyonun iptal edildiğini doğrula
  await expect(operationPromise).rejects.toThrow('Operation aborted');
  expect(longRunningOperationCompleted).toBe(false);
});


Bu yaklaşım, her testin kendi AbortSignal'ına sahip olmasını ve test sonunda otomatik olarak temizlenmesini sağlar.

5. Gelişmiş AbortSignal Kullanım Teknikleri

AbortSignal'ı daha karmaşık senaryolarda kullanarak testlerinizin kontrolünü artırabilirsiniz.

5.1. AbortSignal'ları Zincirleme (Chaining)

Bazen birden fazla iptal koşulunu yönetmek isteyebilirsiniz. Örneğin, hem genel bir test zaman aşımı hem de belirli bir operasyon için daha kısa bir zaman aşımı. AbortSignal.any() (deneysel bir API olsa da, benzer mantık manuel olarak uygulanabilir) veya birden fazla sinyali dinleyen bir yapı oluşturarak bu durumu yönetebilirsiniz.

// Birden fazla sinyali dinleyen bir Promise örneği
function raceSignals(signals: AbortSignal[]): Promise {
  return new Promise((resolve, reject) => {
    const onAbort = () => {
      signals.forEach(s => s.removeEventListener('abort', onAbort));
      reject(new Error('One of the signals aborted'));
    };
    signals.forEach(s => s.addEventListener('abort', onAbort, { once: true }));
  });
}

test('Birden fazla AbortSignal ile zincirleme', async ({ page }) => {
  const globalController = new AbortController();
  const specificController = new AbortController();

  // 1. Genel test sinyali (örneğin, test fixture'dan gelen)
  // 2. Özel operasyon sinyali (daha kısa süreli)
  const combinedSignalPromise = raceSignals([globalController.signal, specificController.signal]);

  let operationCompleted = false;
  const longRunningPromise = new Promise((resolve, reject) => {
    const timeoutId = setTimeout(() => {
      operationCompleted = true;
      resolve();
    }, 10000); // 10 saniye sürecek operasyon

    combinedSignalPromise.catch(error => {
      clearTimeout(timeoutId);
      reject(error);
    });
  });

  // Global sinyali 5 saniye sonra iptal et
  setTimeout(() => globalController.abort(), 5000);

  // Özel sinyali 2 saniye sonra iptal et (bu önce tetiklenecek)
  setTimeout(() => specificController.abort(), 2000);

  await page.goto('https://www.example.com');

  await expect(longRunningPromise).rejects.toThrow('One of the signals aborted');
  expect(operationCompleted).toBe(false);
});


Bu senaryoda, specificController 2 saniye sonra tetiklendiği için longRunningPromise de 2 saniye sonra iptal olacaktır.

5.2. Yardımcı Fonksiyonlar Aracılığıyla AbortSignal Yayma

Karmaşık testlerde, asenkron işlemleri gerçekleştiren yardımcı fonksiyonlarınız olabilir. AbortSignal'ı bu fonksiyonlara parametre olarak geçirerek iptal mekanizmasını tüm katmanlara yayabilirsiniz.

// helper.ts
export async function performLongOperation(signal: AbortSignal): Promise {
  return new Promise((resolve, reject) => {
    const timeoutId = setTimeout(() => {
      if (signal.aborted) {
        reject(new Error('Operation aborted before completion'));
        return;
      }
      resolve('Operasyon başarıyla tamamlandı.');
    }, 4000); // 4 saniye süren bir işlem

    signal.addEventListener('abort', () => {
      clearTimeout(timeoutId);
      reject(new Error('Operation aborted by signal'));
    }, { once: true });
  });
}

// test.ts
import { test, expect } from '@playwright/test';
import { performLongOperation } from './helper';

test('Yardımcı fonksiyon ile AbortSignal kullanımı', async ({ page }) => {
  const controller = new AbortController();
  const signal = controller.signal;

  // 2 saniye sonra operasyonu iptal et
  setTimeout(() => controller.abort(), 2000);

  await page.goto('https://www.example.com');

  await expect(performLongOperation(signal)).rejects.toThrow('Operation aborted by signal');
});


Bu, kod tekrarını azaltır ve iptal mantığını merkezi bir şekilde yönetmenizi sağlar.

6. En İyi Uygulamalar ve Dikkat Edilmesi Gerekenler

AbortSignal kullanırken bazı önemli noktaları göz önünde bulundurmak, testlerinizin daha verimli ve hatasız olmasını sağlar.

6.1. Ne Zaman Kullanmalı, Ne Zaman Kullanmamalı?

Kullanım Durumu Açıklama
✅ Uzun Süreli Asenkron İşlemler Manuel fetch istekleri, setTimeout/setInterval gibi özel zamanlayıcılar.
✅ Kaynak Temizliği Test sonunda tüm bekleyen işlemlerin durdurulması gereken durumlar.
✅ Dinamik İptal Koşulları Belirli bir kullanıcı etkileşimi veya koşula bağlı olarak iptal edilmesi gereken işlemler.
❌ Playwright'ın Dahili Bekleme Metotları page.waitForSelector, page.waitForLoadState gibi metotlar kendi zaman aşımı mekanizmalarına sahiptir ve genellikle AbortSignal'a ihtiyaç duymazlar.
❌ Çok Kısa Süreli İşlemler Çok hızlı tamamlanan işlemler için AbortSignal kullanmak, ek yük getirebilir ve gereksiz olabilir.

6.2. İptal Edilen Operasyonlarda Hata Yönetimi

Bir operasyon AbortSignal tarafından iptal edildiğinde, genellikle bir AbortError hatası fırlatılır. Bu hatayı doğru bir şekilde yakalamak ve işlemek önemlidir. Bu, test raporlarınızda "gerçek" hatalar ile "kasıtlı iptaller" arasındaki farkı anlamanıza yardımcı olur. try...catch blokları kullanarak AbortError'u yakalayın ve buna göre davranın (örneğin, testin başarısız olmasını engellemek veya uygun bir mesaj loglamak).

6.3. Performans Etkileri

AbortController ve AbortSignal nesneleri oluşturmak ve olay dinleyicileri eklemek, çok küçük bir performans yükü getirir. Ancak, bu yük genellikle uzun süreli asenkron işlemlerin iptal edilmesinden elde edilen performans kazancına kıyasla ihmal edilebilir düzeydedir. Gereksiz yere her küçük işlem için AbortSignal kullanmaktan kaçınmak, en iyi performansı sağlamanıza yardımcı olacaktır. Sadece gerçekten iptal edilebilir olması gereken veya kaynak sızıntısına yol açabilecek operasyonlar için kullanın.

7. Alternatif Yaklaşımlar ve AbortSignal Karşılaştırması

AbortSignal, asenkron operasyonları yönetmek için güçlü bir araç olsa da, başka yöntemler de mevcuttur.

7.1. Manuel Temizleme (finally Blokları)

try...finally blokları, bir operasyonun sonunda kaynakları temizlemek için geleneksel bir yöntemdir.

test('Manuel temizleme örneği', async ({ page }) => {
  let timerId: NodeJS.Timeout | null = null;
  try {
    timerId = setTimeout(() => console.log('Zamanlayıcı çalıştı (manuel)'), 5000);
    await page.goto('https://www.example.com');
    // ... test adımları ...
  } finally {
    if (timerId) {
      clearTimeout(timerId);
      console.log('Zamanlayıcı manuel olarak temizlendi.');
    }
  }
});


Bu yöntem, basit senaryolar için yeterli olabilir. Ancak, iç içe geçmiş veya çok sayıda asenkron işlemde, her birini manuel olarak takip etmek ve temizlemek karmaşık ve hataya açık hale gelebilir. AbortSignal, bu temizleme mantığını merkezi ve olay tabanlı bir şekilde yönetir.

7.2. Playwright'ın Dahili Zaman Aşımları

Playwright, testler ve sayfa işlemleri için yerleşik zaman aşımı mekanizmalarına sahiptir:

  • test.setTimeout(ms): Tüm testin zaman aşımını ayarlar.
  • page.setDefaultTimeout(ms): Sayfa üzerindeki tüm işlemlerin varsayılan zaman aşımını ayarlar.
  • page.waitFor* metotları: Kendi zaman aşımı seçeneklerine sahiptir.

Bu zaman aşımları, Playwright'ın kontrolündeki operasyonları otomatik olarak sonlandırır. Ancak, AbortSignal'ın farkı, Playwright'ın ötesindeki (örneğin, doğrudan JavaScript fetch veya setTimeout çağrıları) asenkron işlemleri de kontrol edebilmesidir. AbortSignal, daha granular ve operasyon bazında bir iptal mekanizması sunar. Playwright'ın zaman aşımları testin tamamını veya belirli bir Playwright API çağrısını sonlandırırken, AbortSignal bir veya daha fazla bağımsız asenkron operasyonu hedefleyebilir.

7.3. Neden AbortSignal Daha Granüler Kontrol Sağlar?

AbortSignal, asenkron operasyonların iptalini doğrudan operasyonun kendisine entegre etme yeteneği sunar. Bu, bir operasyonun ne zaman ve hangi koşullar altında iptal edileceğine dair daha ince taneli kontrol sağlar. Örneğin, bir ağ isteğinin belirli bir koşul karşılandığında iptal edilmesini sağlayabilir veya bir kullanıcının sayfadan ayrılması durumunda arka plandaki bir işlemi durdurabilirsiniz. Playwright'ın dahili zaman aşımları genellikle "her şeyi durdur" veya "bu belirli Playwright eylemini durdur" yaklaşımını benimserken, AbortSignal "bu belirli asenkron JavaScript işlemini durdur" yeteneği sunar. Bu esneklik, karmaşık test senaryolarında kaynak yönetimini çok daha etkili hale getirir.

Sonuç

Playwright testlerinde kaynak sızıntıları, testlerin güvenilirliğini ve performansını ciddi şekilde etkileyebilir. AbortSignal ve AbortController, bu sorunları çözmek için modern ve standart bir yaklaşımdır. Bu API'yi kullanarak, testlerinizdeki asenkron ağ isteklerini, zamanlayıcıları ve diğer uzun süreli işlemleri proaktif bir şekilde iptal edebilir, böylece test ortamınızın temiz kalmasını ve testlerinizin daha hızlı ve daha kararlı çalışmasını sağlayabilirsiniz. AbortSignal'ı test stratejinize dahil etmek, daha sağlam, bakımı kolay ve verimli test otomasyonu süreçleri oluşturmanıza yardımcı olacaktır.

SSS (Sık Sorulan Sorular)

AbortSignal nedir?

AbortSignal, JavaScript'teki asenkron operasyonları iptal etmek için kullanılan bir web API'sidir. Bir AbortController tarafından oluşturulur ve abort() metodu çağrıldığında ilişkili operasyonlara iptal sinyali gönderir.

AbortSignal Playwright'a özel mi?

Hayır, AbortSignal Playwright'a özel değildir. Web platformunun standart bir parçasıdır ve fetch API'si gibi birçok tarayıcı API'si tarafından desteklenir. Playwright testlerinde, test içindeki JavaScript asenkron operasyonlarını

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.