Bölüm 8 / 12
Uçtan uca test
Playwright.
13 dakikalık okuma
Piramidin tepesine geldik. Birim ve entegrasyon testleri parçaları
ve bağlantıları kanıtladı; ama "vatandaş tarayıcıyı açtı, formu doldurdu, başvurusunu
gönderdi ve sonucu gördü" cümlesini yalnızca uçtan uca test kanıtlayabilir: gerçek
tarayıcı, gerçek frontend, gerçek API, gerçek veritabanı — hepsi birlikte. Bu katmanın
tarihi kararsız testlerle yazıldı; modern araç Playwright, o tarihin derslerini
mimarisine gömdü: elemanı bekleyen locator'lar, kendiliğinden bekleyen eylemler,
yeniden deneyen web-first assertion'lar. Bu derste Playwright'ı kurup ilk akış
testini yazacak, kimlik durumunu storageState ile bir kez kurup her teste
taşıyacak, kırılan testi trace viewer ile saniyeler içinde teşhis edecek ve E2E'nin
en zor disiplinini öğreneceksin: az sayıda, bağımsız, kararlı test.
01Ne öğreneceksin
- E2E'nin yeri: ne zaman değer üretir, ne zaman yük olur
- Playwright mimarisi: üç tarayıcı motoru, koşucu, yapılandırma,
webServer - Locator felsefesi:
getByRole, tembel değerlendirme, auto-wait ve eylem ön koşulları - Web-first assertion: kendiliğinden yeniden deneyen doğrulama;
waitForTimeoutyasağı storageStateile kimliği bir kez kurup tüm testlere taşımak- Trace viewer / UI mode ile kırılan testin teşhisi
- Düzen: Page Object ve fixture dozu; paralel worker'lar ve test bağımsızlığı
- Kararlılık disiplini ve CI'da koşum (18. hat köprüsü); Selenium karşılaştırması
02Tepenin ekonomisi
2. dersin tablosunu hatırla: E2E en yüksek güveni verir — ve en yavaş, en pahalı, en kırılgan katmandır. Hata lokalizasyonu da en zayıftır: kırmızı bir E2E "akış bozuk" der; frontend mi, API mi, veritabanı mı, test ortamı mı — söylemez. Bu ekonomiden iki kural çıkar:
- Az ve öz: E2E'ye yalnız kritik kullanıcı yolculukları yazılır — başvuru yapma, sonuç görüntüleme, belge yükleme, giriş/çıkış. "Her doğrulama mesajının E2E'si" dondurma külahına giden yoldur; alan doğrulaması birimde, uç sözleşmesi entegrasyonda zaten kanıtlandı. Ölçü yine aynı: bir davranışı yakalayabilecek en ucuz katmanda test et; E2E, katmanların toplamının veremeyeceği tek şeyi — bütünün kullanıcı gözüyle çalıştığını — kanıtlasın.
- Sıfır tolerans kararlılığa: E2E katmanında kararsız test normalleştiği an katman ölür — "bir daha koştur" kültürü gerçek kırmızıyı da örter (2. ders). Bu dersin araç seçiminden yazım kurallarına her parçası, bu tek düşmana karşı kurgulanmıştır.
Terminoloji notu: yazılıda karşına sistem testi (bütünün gereksinimlere karşı testi) ve kabul testi (müşterinin/işin "bu iş bitti mi" onayı, 5. dersteki çift döngünün dış halkası) çıkar; otomatik E2E, ikisinin de modern uygulama biçimidir — kullanıcı yolculuğunu gereksinim diliyle doğrular.
03Playwright: kurulum ve mimari
npm init playwright@latest # koşucu + config + örnek test + tarayıcı indirme
npx playwright test # tüm testler, başsız (headless)
npx playwright test --ui # UI mode: izleyerek koştur
npx playwright show-report # HTML rapor
Playwright üç tarayıcı motorunu tek API ile sürer: Chromium, Firefox,
WebKit (Safari'nin motoru) — aynı test üçünde de koşar; yapılandırmadaki
projects bölümü bunu yönetir. Sürüş, WebDriver gibi HTTP tabanlı bir aracı
protokol yerine tarayıcıyla doğrudan, olay tabanlı bir kanaldan yapılır; "eleman hazır mı"
bilgisi anlık aktığı için beklemeler isabetlidir. İşe yarayan iki yapılandırma parçası:
// playwright.config.ts
export default defineConfig({
use: {
baseURL: 'http://localhost:5000',
trace: 'on-first-retry', // kırılan testin filmi (06. bölüm)
},
webServer: { // testten önce uygulamayı Playwright başlatır
command: 'dotnet run --project ../Basvuru.Api',
url: 'http://localhost:5000',
reuseExistingServer: !process.env.CI,
},
});
webServer küçük ama kıymetli: "önce uygulamayı elle ayağa kaldır" adımını
ortadan kaldırır — test koşusu tek komuta iner, CI'da da aynen çalışır.
04Locator felsefesi: kararlılığın mimarisi
Kararsız E2E'nin klasik iki nedeni vardı: eleman daha DOM'a gelmeden ona uzanmak ve
kırılgan seçiciler (#app > div:nth-child(3) > span). Playwright ikisini
de mimariyle çözer.
Locator tembeldir: page.getByRole('button', { name: 'Başvur' })
elemanı o anda aramaz; bir tarif tutar. Arama, eylem anında yapılır ve
auto-wait devreye girer: Playwright eleman görünür, etkin, sabit (animasyonu
bitmiş) ve tıklamayı gerçekten alabilir olana dek — zaman aşımına kadar — kendiliğinden
bekler. "Elemanı bekle, sonra tıkla" diye iki adım yazdığın Selenium düzeninin tamamı,
burada tek satırın içindedir.
Seçici öncelikleri 7. derstekiyle aynıdır ve bu bir tesadüf değildir — Testing
Library'nin felsefesi buraya da taşındı: önce getByRole (erişilebilirlik
ağacı; 8. hattaki semantik HTML yine kâr payı ödüyor), sonra getByLabel,
getByPlaceholder, getByText; kaçınılmazsa
getByTestId. CSS/XPath en son çaredir çünkü yapıya bağlanır — görsel refactor
testi kırar.
Web-first assertion'lar üçüncü ayaktır: await expect(locator).toBeVisible(),
toHaveText, toHaveURL gibi doğrulamalar tek seferlik bakmaz;
beklenti sağlanana ya da zaman aşımına dek yeniden dener. Asenkron arayüzde
"assert anında henüz gelmemişti" sınıfı kararsızlığı kökünden keser. Bu üçlünün doğal
sonucu da dersin en sert kuralıdır: page.waitForTimeout(3000) yasak.
Sabit uyku, yavaş günde yine kırılır, hızlı günde zaman çalar; her uyku bir "neyi
beklediğimi bilmiyorum" itirafıdır. Neyi bekliyorsan onu bekle: locator, assertion
ya da waitForURL.
05İlk akış testi
import { test, expect } from '@playwright/test';
test('vatandaş başvuru yapabilir', async ({ page }) => {
await page.goto('/ilanlar');
await page.getByRole('link', { name: 'Mühendis (MUH-2025)' }).click();
await page.getByLabel('E-posta').fill('aday@ornek.gov.tr');
await page.getByLabel('Doğum tarihi').fill('2001-03-15');
await page.getByRole('button', { name: 'Başvur' }).click();
await expect(page).toHaveURL(/basvuru-alindi/);
await expect(page.getByRole('heading', { name: /başvurunuz alındı/i })).toBeVisible();
await expect(page.getByText(/başvuru numaranız/i)).toBeVisible();
});
test('boş formda alan hataları görünür', async ({ page }) => { // negatif yol (2. ders)
await page.goto('/ilanlar/muh-2025/basvur');
await page.getByRole('button', { name: 'Başvur' }).click();
await expect(page.getByText('E-posta zorunludur')).toBeVisible();
});
Dikkat çekenler: tek bir await ...click() satırında gezinme + bekleme +
eylem birleşik; assertion'lar sonucu kullanıcı diliyle doğruluyor (URL, başlık, görünen
metin); ve iki test birbirinden tamamen bağımsız — her biri kendi sayfasına kendisi
gidiyor. Her test() taze bir tarayıcı bağlamı (context) alır: çerezler,
localStorage, oturum — hepsi sıfırdan; izolasyonun tarayıcı katmanındaki karşılığı budur
(3. dersteki "her teste yeni sınıf örneği"nin birebir dengi).
06Kimlik: storageState ile bir kez gir
Testlerin çoğu giriş yapılmış kullanıcı ister; her testin form üzerinden giriş yapması
hem yavaştır hem giriş sayfasını tek hata noktası yapar (giriş bozulursa tüm
paket kırmızı — teşhis gücü sıfır). Playwright deseni: girişi bir kez, ayrı bir
kurulum projesinde yap; oturumu (çerez + localStorage) storageState dosyasına
kaydet; tüm testler o dosyayla, hazır oturumla başlasın:
// auth.setup.ts
setup('giriş yap', async ({ page }) => {
await page.goto('/giris');
await page.getByLabel('E-posta').fill(process.env.TEST_KULLANICI!);
await page.getByLabel('Parola').fill(process.env.TEST_PAROLA!); // sır koda gömülmez (13-10)
await page.getByRole('button', { name: 'Giriş' }).click();
await expect(page.getByRole('link', { name: 'Başvurularım' })).toBeVisible();
await page.context().storageState({ path: '.auth/aday.json' });
});
// config: setup projesi + ona bağımlı test projesi
projects: [
{ name: 'setup', testMatch: /auth\.setup\.ts/ },
{ name: 'chromium', use: { storageState: '.auth/aday.json' }, dependencies: ['setup'] },
]
Giriş akışının kendisi elbette test edilir — ama bir kez, kendi testinde;
diğer yüz test onu tekrar etmez. Farklı roller için (aday ↔ denetçi) ayrı state dosyaları
tutulur; 6. dersteki 403/IDOR senaryolarının E2E karşılığı, iki farklı state ile aynı
sayfaya gitmektir. .auth/ klasörü depoya girmez (13-10: oturum da sırdır).
07Teşhis: trace viewer ve UI mode
E2E'nin teşhis zafiyetine Playwright'ın cevabı, koşunun filmini kaydetmektir.
trace: 'on-first-retry' ayarıyla, kırılıp yeniden denenen her test için bir
trace dosyası düşer; npx playwright show-trace onu açar: zaman
çizelgesinde her adımın ekran görüntüsü, o andaki DOM anlık kopyası (üzerinde locator
deneyebilirsin), ağ istekleri, konsol logları. "CI'da kırıldı, bende geçiyor" vakasının
klasik çözümsüzlüğü — yeniden üretememek — burada ortadan kalkar: kırılan koşunun
kendisi elindedir. CI, trace ve raporu artifact olarak saklar (18. hatta bağlanacak).
Geliştirme sırasındaki araç ise UI mode'dur (--ui): testleri
izleyerek koşturur, her adımda durup DOM'a bakarsın; --debug aynı işi adım
adım yürütmeyle yapar. Bir de tersi var: npx playwright codegen tıklamalarını
koda çevirir — başlangıç için iskele çıkarır, ama ürettiği seçiciler her zaman 04. bölümün
süzgecinden geçirilir; kayıt aracı mühendisliğin yerini tutmaz.
08Düzen: Page Object, fixture, paralellik
Testler çoğaldıkça aynı sayfa etkileşimleri tekrar eder; klasik çözüm Page Object desenidir — sayfanın locator'ları ve eylemleri bir sınıfta toplanır:
export class BasvuruSayfasi {
constructor(private page: Page) {}
eposta = () => this.page.getByLabel('E-posta');
basvur = () => this.page.getByRole('button', { name: 'Başvur' });
async doldurVeGonder(eposta: string) {
await this.eposta().fill(eposta);
await this.basvur().click();
}
}
Doz uyarısı: Page Object bir soyutlama vergisidir — locator değişince tek yerde
düzeltirsin (kazanç), ama testin ne yaptığı bir dolaylama arkasına saklanır (bedel).
Küçük paketlerde çıplak locator'lar gayet meşrudur; desen, tekrar gerçekten
ağrıtınca gelir (Hat 4'ün YAGNI'si). Playwright'ın test.extend fixture'ları
da aynı işi kompozisyonla yapar: hazır sayfa nesneleri, hazır test verisi, hazır oturum —
xUnit fixture kavramının (3. ders) buradaki karşılığı.
Paralellik: Playwright test dosyalarını ayrı worker süreçlerinde paralel
koşturur. Bunun tek ön şartı 05. bölümde gördüğün bağımsızlıktır: her test kendi verisini
kurar, ortak kayda yaslanmaz — aksi hâlde 6. dersteki izolasyon sorunları tarayıcı
ölçeğinde geri gelir. test.describe.configure({ mode: 'serial' }) diye bir
acil çıkış vardır ve kötü kokudur: testleri sıraya bağlamak, birinin kırılmasının
diğerlerini anlamsızlaştırması demektir. Retry politikası da aynı dozla kullanılır: CI'da
retries: 1-2 ağ türbülansına karşı makul bir amortisördür, ama retry ile
geçen test bir borçtur — trace'i incelenir, nedeni kapatılır; "retry var nasılsa"
kültürü kararsızlığı normalleştirir.
09Selenium'a kıyasla — ve CI penceresi
| Selenium WebDriver | Playwright | |
|---|---|---|
| Sürüş | W3C WebDriver protokolü; tarayıcı başına ayrı sürücü (chromedriver, geckodriver) | Doğrudan, olay tabanlı kanal; sürücü kurulumu yok, motorları kendisi indirir |
| Bekleme | Varsayılan yok; explicit/implicit wait'i sen kurarsın — tarihsel kararsızlığın ana kaynağı | Auto-wait + web-first assertion mimaride |
| Araç seti | Çekirdek kütüphane; koşucu, rapor, paralellik ayrı parçalardan derlenir | Koşucu, trace, rapor, paralellik tek pakette |
| Dil/ekosistem | Çok geniş dil desteği; devasa kurulu taban, mevcut kurumsal paketlerde hâlâ standart | TS/JS birinci sınıf (.NET/Java/Python bağları da var); yeni projelerin fiilî tercihi |
Dürüst özet: Selenium'un tarihsel değeri ve kurulu tabanı büyüktür; mülakatta "Selenium?" sorusuna doğru cevap, kavramların ortak olduğunu (seçici, bekleme, sayfa nesnesi) ve farkın beklemenin kimin sorumluluğu olduğunda yattığını söylemektir. CI tarafına şimdilik bir pencere: E2E paketi, dağıtım hattında entegrasyon testlerinden sonra, tercihen dağıtım sonrası bir duman (smoke) koşusu olarak da koşar; tarayıcılı koşu için hazır konteyner imajları kullanılır, trace/rapor artifact olarak saklanır — mekanizması 18. hattın işi.
10Mini alıştırma — ilk akışın filmi
Yaklaşık 20 dakika · npm + herhangi bir web projen (yoksa bu site!)
- Kur.
npm init playwright@latest; config'ebaseURLyaz. Test hedefi olarak kendi projenin bir akışını seç — elinde yoksa bu çalışma sitesini kullan: "ana haritadan bir hatta gir, ilk dersi aç, ders kutusundan tamamlandı işaretle". - Akışı yaz. Testi yalnız
getByRole/getByTextlocator'larıyla yaz; sonucu URL + görünen metinle doğrula. CSS seçici kullanmadan bitirebildin mi? Kullanmak zorunda kaldığın yer, sayfanın semantik eksiğidir — not et. - Negatif yol. Bir de olmaması gerekeni doğrula: var olmayan bir sayfaya gidince 404 içeriği, ya da boş form gönderiminde hata metni.
- Filmi izle. Testi bilerek kır (yanlış metin bekle),
--trace onile koştur veshow-traceile aç: kırılma anındaki DOM'da doğru locator'ı trace içinden bul. Teşhisin kaç saniye sürdüğünü gör. - Uyku provası. Teste
waitForTimeout(3000)ekle, koşu süresine etkisine bak, sonra sil ve yerine neyi beklediğini söyleyen bir assertion koy. Farkı bir cümleyle yaz: sabit süre ↔ koşul bekleme. - Paralellik denemesi. Testi ikinci bir dosyaya kopyala (farklı senaryoyla), koşunun iki worker'da paralel gittiğini raporda gör. İki test ortak bir duruma yazsaydı ne olurdu — 6. dersteki cevabı buraya bağla.
11Kendini sına
E2E test ne zaman değer üretir, ne zaman yüktür? "Az sayıda E2E" kuralının gerekçesini katman ekonomisiyle açıkla.
E2E, sistemi kullanıcının gördüğü yerden — gerçek tarayıcı, gerçek frontend, gerçek API ve veritabanıyla — sürer ve tek benzersiz kanıtı verir: bütün, kullanıcı gözüyle çalışıyor. Bedeli katman ekonomisinin en pahalı ucudur: en yavaş koşu, en yüksek yazım/bakım maliyeti, en kırılgan altyapı ve en zayıf hata lokalizasyonu — kırmızı E2E "akış bozuk" der ama frontend mi, API mi, ortam mı söylemez; teşhis bir seans ister. Bu yüzden değer ürettiği yer, az sayıda kritik kullanıcı yolculuğudur: başvuru yapma, giriş, sonuç görüntüleme gibi bozulması en pahalı akışlar ve dağıtım sonrası duman koşusu. Yüke dönüştüğü yer, alt katmanların işini üstlenmesidir: her alan doğrulamasının, her uç sözleşmesinin E2E ile test edilmesi dondurma külahı üretir — saatler süren, sürekli kırılan, kimsenin bakmadığı koşular. Kural: her davranış onu yakalayabilecek en ucuz katmanda test edilir; E2E yalnız katmanların toplamının veremeyeceğini kanıtlar. İkinci şart kararlılıktır: bu katmanda kararsız test normalleşirse "bir daha koştur" kültürü gerçek hatayı da örter; retry ile geçen her test incelenmesi gereken bir borçtur.
Playwright'ta locator, auto-wait ve web-first assertion nasıl birlikte çalışır? waitForTimeout neden yasaktır?
Locator tembel bir tariftir: getByRole('button', { name:
'Başvur' }) yazıldığı anda DOM'da arama yapmaz; eylem anında değerlendirilir. O
anda auto-wait devreye girer: Playwright, eleman görünür, etkin, sabit (animasyon bitmiş)
ve etkileşimi gerçekten alabilir olana dek zaman aşımı sınırında kendiliğinden bekler —
"önce bekle sonra tıkla" ritüeli tek satıra gömülüdür. Web-first assertion'lar
(await expect(locator).toBeVisible(), toHaveText,
toHaveURL) tek seferlik bakmaz; beklenti sağlanana dek yeniden dener —
asenkron arayüzde "assert anında henüz gelmemişti" kararsızlığını kökünden keser. Seçici
önceliği Testing Library ile aynıdır: önce rol + erişilebilir isim, sonra etiket/metin;
CSS/XPath yapıya bağlandığı için son çaredir. waitForTimeout yasaktır çünkü
sabit uyku iki yönde de yanlıştır: yavaş günde yine kırılır (kararsızlık çözülmez), hızlı
günde koşuyu boşuna bekletir; ayrıca her uyku "neyi beklediğimi bilmiyorum" itirafıdır ve
gerçek nedeni maskeleyerek borcu büyütür. Doğrusu, beklenen koşulun kendisini beklemektir:
locator eylemi, assertion ya da waitForURL. Kararlılığın mimariye gömülmesi,
Playwright'ı bekleme sorumluluğunu geliştiriciye bırakan WebDriver düzeninden ayıran ana
çizgidir.
storageState deseni nasıl çalışır ve neden her testte form üzerinden giriş yapılmaz? Farklı roller ve güvenlik senaryoları E2E'de nasıl kurulur?
Desen: giriş, ayrı bir kurulum (setup) projesinde bir kez form
üzerinden yapılır; başarı doğrulanır ve oturumun tarayıcı durumu — çerezler +
localStorage — context().storageState({ path }) ile bir dosyaya kaydedilir;
asıl test projesi config'te bu dosyayı storageState olarak kullanır ve
dependencies: ['setup'] ile kurulumdan sonra koşar — her test hazır oturumla
başlar. Her testte giriş yapmamanın iki nedeni var: hız (giriş akışı yüz kez tekrar eder)
ve teşhis gücü (giriş sayfası bozulduğunda tüm paket kırmızıya boyanır — tek hata yüz
kırmızı üretir, neden görünmez). Girişin kendisi elbette test edilir, ama bir kez, kendi
testinde. Farklı roller için ayrı state dosyaları tutulur (aday.json, denetci.json);
yetki senaryolarının E2E karşılığı iki farklı state ile aynı sayfaya gitmektir — adayın
görmemesi gereken yönetim bağlantısının yokluğu queryBy mantığıyla, yetkisiz
sayfanın engellendiği görünür davranışla doğrulanır (asıl 401/403/IDOR yükü daha ucuz
katmanda, entegrasyon testindedir). Güvenlik hijyeni: test kimlik bilgileri ortam
değişkeninden gelir, state dosyaları depoya girmez — oturum da bir sırdır.
Trace viewer neyi çözer? Page Object'in dozu nedir ve Playwright ile Selenium arasındaki temel farkları nasıl özetlersin?
Trace viewer, E2E'nin en zayıf yanını — teşhisi — çözer:
trace: 'on-first-retry' ile kırılan koşunun filmi kaydedilir; zaman
çizelgesinde her adımın ekran görüntüsü, o anki DOM anlık kopyası (üzerinde locator bile
denenir), ağ istekleri ve konsol logları vardır. "CI'da kırılıyor, bende geçiyor"
vakasının çözümsüzlüğü yeniden üretememekti; trace ile kırılan koşunun kendisi elindedir,
CI bunu artifact olarak saklar. Geliştirmede UI mode (--ui) adım adım
izleme sağlar; codegen iskele çıkarır ama ürettiği seçiciler locator süzgecinden
geçirilir. Page Object, sayfa etkileşimlerini tek sınıfta toplar: locator değişince tek
yer düzelir — ama testin ne yaptığını dolaylamanın arkasına saklar; küçük pakette çıplak
locator meşrudur, desen tekrar gerçekten ağrıtınca gelir (YAGNI). Playwright'ın
test.extend fixture'ları aynı ihtiyacı kompozisyonla karşılar. Selenium
karşılaştırması üç cümledir: Selenium W3C WebDriver protokolü ve tarayıcı başına ayrı
sürücüyle çalışır, bekleme sorumluluğu geliştiricidedir (tarihsel kararsızlığın kaynağı)
ve çekirdek kütüphanedir — koşucu/rapor ayrı derlenir; Playwright doğrudan olay tabanlı
kanalla sürer, auto-wait + web-first assertion mimaridedir ve koşucu-trace-paralellik tek
pakettir. Kavramlar (seçici, bekleme, sayfa nesnesi) ortaktır; fark, beklemenin kimin
sorumluluğu olduğudur.
12Sınav köşesi
Yazılıda nasıl sorulur
(1) Uçtan uca (E2E) test nedir? (2) Sistem testi ve kabul testi nedir, farkları nelerdir? (3) E2E testler neden az sayıda tutulmalıdır? (4) Playwright nedir, hangi tarayıcıları destekler? (5) Selenium ile Playwright arasındaki farklar nelerdir? (6) Test otomasyonunda bekleme (wait) stratejileri nelerdir? (7) Page Object deseni nedir? (8) Kararsız (flaky) E2E testlerinin nedenleri ve çözümleri nelerdir? (9) E2E testlerde oturum/kimlik yönetimi nasıl yapılır? (10) Duman (smoke) testi nedir?
Sözlüde nasıl anlatılır — 2 dakikalık taslak
Düzen: (1) Yer — "E2E'yi piramidin tepesinde, az sayıda kritik kullanıcı yolculuğu için kullanırım: başvuru, giriş, sonuç. Alan doğrulaması birimde, uç sözleşmesi entegrasyonda zaten kanıtlı — E2E yalnız bütünün çalıştığını kanıtlar." (2) Araç — "Playwright kullanırım: üç motoru tek API ile sürer, locator'ları tembeldir ve auto-wait ile eylem anında bekler; assertion'ları web-first'tür, beklenti sağlanana dek yeniden dener. Bu yüzden pakette tek bir waitForTimeout yoktur — sabit uyku, neyi beklediğini bilmemektir." (3) Seçici — "Elemanları getByRole ile erişilebilirlik ağacından bulurum; CSS/XPath yapıya bağlanır, görsel refactor'da kırılır." (4) Operasyon — "Girişi setup projesinde bir kez yapar, storageState ile tüm testlere taşırım; testler bağımsızdır, worker'larda paralel koşar. Kırılan testin trace'ini açarım: adım adım ekran, DOM, ağ — 'bende geçiyor' tartışması biter." (5) Kıdem vuruşu — "Bu katmanda kararsızlığa sıfır tolerans uygularım: retry ile geçen test kapatılmış değil ertelenmiş bir borçtur, trace'ini inceler nedenini kapatırım. Selenium sorulursa kavramların ortak olduğunu, farkın beklemenin kimin sorumluluğunda olduğu söylerim."
