Playwright'a Giriş: Playwright Test mi Playwright for .NET mi, Locator'lar, Auto-wait ve Trace Viewer
Playwright action'dan önce element'i bekler ama action'ın sonucunu beklemez; sonucu expect(locator) ile kurulan web-first assertion'lara bırak. .NET + Angular ekibi için Playwright Test ile Microsoft.Playwright'ı karşılaştırıyor, getByRole ve getByLabel ile kullanıcı gözüyle locator yazmayı, strict mode'u, codegen'i ve kalan bir testi trace viewer ile okumayı gerçek koşularla gösteriyoruz.
Sürüm notu: Bu yazıdaki kodlar .NET 10, Angular 22.2, Playwright 1.65 (@playwright/test), Microsoft.Playwright 1.64.1, xUnit.net v3 4.0.3, Testcontainers for .NET 4.16 ve SQL Server 2025 ile derlenip denenmiştir.
Önce Tahmin Et
Bookstore'un Angular arayüzünde katalog sayfası açılırken bütün kitapları listeliyor; arama kutusuna bir şey yazıp "Search" düğmesine basınca liste API'den gelen sonuçla yenileniyor. Test veritabanında dört kitap var: Dune, Emma, The Hobbit ve Middlemarch. İlk Playwright testini şöyle yazdın:
test('count read once', async ({ page }) => {
await page.goto('/');
await page.getByLabel('Search books').fill('dune');
await page.getByRole('button', { name: 'Search' }).click();
const results = page.getByRole('list', { name: 'Search results' }).getByRole('listitem');
expect(await results.count()).toBe(1);
});
Playwright'ın her action'dan önce beklediğini duydun; click bitti, sonuç listesi de bir sonraki satırda okunuyor. Test geçer mi? Kalırsa count() hangi sayıyı döndürmüş olur?
Cevap: Playwright Action'ı Bekler, Sonucu Değil
Test kaldı:
✘ 1 [chromium] › e2e/demo06.spec.ts:3:1 › count read once (474ms)
Error: expect(received).toBe(expected) // Object.is equality
Expected: 1
Received: 4
Dört, aramadan önceki listenin uzunluğu. Playwright click'ten önce düğmenin görünür, sabit ve tıklanabilir olmasını bekledi ve tıkladı; ama tıklamanın sonucu olan HTTP isteğini ve Angular'ın listeyi yeniden çizmesini beklemek onun işi değil. count() o anda DOM'da (tarayıcının sayfayı tuttuğu element ağacı) ne varsa onu sayan tek seferlik bir okuma. Angular 22 zoneless çalıştığı için change detection bir sonraki tura planlanıyor; sayım yapıldığında ekranda hâlâ eski dört kitap vardı.
Doğrusu, beklemeyi assertion'a bırakmak:
await expect(results).toHaveCount(1);
expect(locator) ile kurulan assertion'lara Playwright "web-first assertion" diyor: koşul sağlanana kadar locator'ı tekrar tekrar değerlendiriyor, varsayılan olarak 5 saniye. Aynı test bu satırla her koşuda geçti. Bu yazının geri kalanı bu ayrımın üzerine kurulu: Playwright'ı hangi dille kullanacağımız, locator'ları nasıl seçeceğimiz ve test kaldığında neye bakacağımız.
Hangi Playwright: Playwright Test mi, Microsoft.Playwright mi?
.NET + Angular ekibinin ilk kararı dil. Playwright aynı tarayıcı otomasyonunu TypeScript, .NET, Java ve Python için sunuyor; ama yalnız Node.js tarafında kendi test runner'ı var. Playwright Test denen bu runner fixture'ları, paralel worker'ları, retry'ı, HTML raporu, uygulamayı test öncesi ayağa kaldıran webServer ayarını ve görsel karşılaştırmayı içeriyor. .NET paketi Microsoft.Playwright ise bir library; testleri xUnit, NUnit ya da MSTest çalıştırıyor. Aynı arama testi C# ile şöyle:
public sealed class CatalogTests(BrowserFixture fixture) : IAsyncLifetime
{
private IBrowserContext context = null!;
private IPage page = null!;
public async ValueTask InitializeAsync()
{
context = await fixture.Browser.NewContextAsync(new BrowserNewContextOptions { BaseURL = fixture.BaseUrl });
page = await context.NewPageAsync();
}
[Fact]
public async Task Search_finds_a_book_with_a_lowercase_term()
{
await page.GotoAsync("/");
await page.GetByLabel("Search books").FillAsync("dune");
await page.GetByRole(AriaRole.Button, new() { Name = "Search" }).ClickAsync();
ILocator results = page.GetByRole(AriaRole.List, new() { Name = "Search results" }).GetByRole(AriaRole.Listitem);
await Expect(results).ToHaveCountAsync(1);
await Expect(results.GetByRole(AriaRole.Heading)).ToHaveTextAsync("Dune");
}
public async ValueTask DisposeAsync() => await context.DisposeAsync();
}
BrowserFixture, 1. yazıdaki SQL Server fixture'ı gibi bir xUnit v3 assembly fixture'ı: Playwright'ı ve Chromium'u bir kez açıyor, her test kendi BrowserContext'ini alıyor. API'nin dili ve locator'lar birebir aynı. Fark araç zincirinde çıktı. İlk koşu Angular uygulaması ayakta değilken yapıldığı için şu hatayla bitti:
Microsoft.Playwright.PlaywrightException : net::ERR_CONNECTION_REFUSED at http://localhost:4200/
Call log:
- navigating to "http://localhost:4200/", waiting until "load"
xUnit tarafında Angular dev server'ını ve API'yi başlatan hazır bir mekanizma yok; ya ayrı terminallerde elle başlatıyorsun ya da fixture'da process yönetimi yazıyorsun. Uygulama ayaktayken test 705 milisaniyede geçti. İkinci fark sürümlerde: yazım anında npm'de @playwright/test 1.65.0 yayında, NuGet'teki Microsoft.Playwright ise 1.64.1'de. İki paket farklı tarayıcı build'leri istiyor; .NET testini 1.65'in tarayıcı klasörüyle çalıştırdığımda şu kutu çıktı:
Microsoft.Playwright.PlaywrightException : Executable doesn't exist at .../chromium_headless_shell-1248/...
╔════════════════════════════════════════════════════════════╗
║ Looks like Playwright was just installed or updated. ║
║ Please run the following command to download new browsers: ║
║ ║
║ pwsh bin/Debug/netX/playwright.ps1 install ║
║ ║
║ <3 Playwright Team ║
╚════════════════════════════════════════════════════════════╝
Her paket sürümü kendi tarayıcı revision'ını istiyor (1.64.1 için Chromium revision 1248, 1.65.0 için 1254). Aynı makinede iki dili birlikte kullanan ekip iki tarayıcı setini indirip güncel tutmak zorunda kalıyor.
| Konu | Playwright Test (TypeScript) | Microsoft.Playwright + xUnit |
|---|---|---|
| Kodun yeri | Angular repo'sunun yanında, aynı package.json | .NET solution'ında ayrı test projesi |
| Uygulamayı başlatmak | webServer ayarı | elle ya da kendi fixture kodun |
| Görsel karşılaştırma | toHaveScreenshot | yok |
| Rapor, UI mode, sharding | runner'ın içinde | trace var; rapor ve sharding CI'a kalıyor |
| Yazım anındaki sürüm | 1.65.0 | 1.64.1 |
| Kime yakın | Angular geliştiricisi | backend geliştiricisi |
Bu seride E2E (uçtan uca) testleri TypeScript ile, Playwright Test'le yazıyorum. Belirleyici olan üç şey: uygulamayı test öncesi ayağa kaldıran webServer (7. yazının konusu), .NET paketinde olmayan toHaveScreenshot (8. yazıda) ve testlerin, kırdığı ekranla aynı repo'da, aynı pull request'te durması. Ekip yalnız backend geliştiricilerinden oluşuyorsa ve E2E testleri az sayıdaysa .NET tarafı da makul bir seçim; locator ve assertion bilgisinin tamamı iki dilde aynı.
Angular CLI tarafına da bakmak gerekiyor. Projede e2e target'ı olmadığında ng e2e şunu söylüyor:
$ npx ng e2e
Cannot find "e2e" target for the specified project.
You can add a package that implements these capabilities.
For example:
Playwright: ng add playwright-ng-schematics
Cypress: ng add @cypress/schematic
Nightwatch: ng add @nightwatch/schematics
WebdriverIO: ng add @wdio/schematics
Puppeteer: ng add @puppeteer/ng-schematics
Angular CLI artık bir E2E framework'üyle gelmiyor; Playwright için önerdiği paket topluluk tarafından yazılmış playwright-ng-schematics. Bu paket ng e2e komutunu Playwright'a bağlayan bir builder ekliyor. Bookstore'da buna gerek görmedim: npm run e2e doğrudan playwright test çalıştırıyor ve Angular sürümü yükseltilirken bir topluluk builder'ının yeni sürümünü beklemek gerekmiyor.
Kurulum ve İlk Testler
Angular projesi 5. yazıdaki web klasöründe duruyor; artık tam bir Angular 22.2 uygulaması: katalog sayfası, sepet ve başlıktaki "Cart (n)" bağlantısı. Playwright devDependency olarak ekleniyor, sürüm sabit:
"scripts": {
"start": "ng serve",
"e2e": "playwright test"
},
"devDependencies": {
"@playwright/test": "1.65.0"
}
Tarayıcılar bir kez npx playwright install chromium ile indiriliyor. Config dosyası:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: [['list'], ['html', { open: 'never' }]],
use: {
baseURL: 'http://localhost:4200',
trace: 'retain-on-failure',
screenshot: 'only-on-failure',
},
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
webServer: [
{
name: 'api',
command: 'dotnet run --project ../tests/Bookstore.E2E.Host',
url: 'http://127.0.0.1:5280/api/books',
reuseExistingServer: !process.env.CI,
timeout: 180_000,
gracefulShutdown: { signal: 'SIGTERM', timeout: 10_000 },
},
{
name: 'web',
command: 'npm start',
url: 'http://localhost:4200',
reuseExistingServer: !process.env.CI,
},
],
});
webServer dizisi iki process başlatıyor: SQL Server container'ını açıp API'yi Kestrel üzerinde 5280 portunda çalıştıran küçük bir .NET host'u ve ng serve. Angular dev server'ı /api isteklerini bir proxy ayarıyla bu porta yönlendiriyor. Host'un nasıl yazıldığı, neden WebApplicationFactory kullandığı ve yolda çıkan sürprizler 7. yazının konusu; bu yazıda yalnız uygulamanın ayakta olduğunu bilmek yetiyor. reuseExistingServer sayesinde iki process'i ayrı terminallerde açık tutarsan Playwright onları yeniden başlatmıyor; testi yazarken her koşuda container beklememek için yerelde böyle çalışıyorum.
İlk üç test, kullanıcının katalogda yaptığı üç şey:
import { expect, test } from '@playwright/test';
test('search finds a book with a lowercase term', async ({ page }) => {
await page.goto('/');
await page.getByLabel('Search books').fill('dune');
await page.getByRole('button', { name: 'Search' }).click();
const results = page.getByRole('list', { name: 'Search results' }).getByRole('listitem');
await expect(results).toHaveCount(1);
await expect(results.getByRole('heading')).toHaveText('Dune');
});
test('adding a book updates the cart link', async ({ page }) => {
await page.goto('/');
const emma = page.getByRole('listitem').filter({ has: page.getByRole('heading', { name: 'Emma' }) });
await emma.getByRole('button', { name: 'Add to cart' }).click();
await expect(page.getByRole('link', { name: 'Cart (1)' })).toBeVisible();
await expect(page.getByRole('status')).toHaveText('Emma added to cart');
});
test('an out-of-stock book cannot be added', async ({ page }) => {
await page.goto('/');
const hobbit = page.getByRole('listitem').filter({ hasText: 'The Hobbit' });
await expect(hobbit.getByText('Out of stock')).toBeVisible();
await expect(hobbit.getByRole('button', { name: 'Add to cart' })).toBeDisabled();
});
Her test kendi page'ini alıyor ve Playwright her test için yeni bir browser context açıyor: cookie'ler, localStorage ve Angular'daki sepet signal'ı testler arasında taşınmıyor. Sepete ekleyen test bu yüzden "Cart (1)" bekleyebiliyor. Üç testi beşer kez, beş worker'la çalıştırdım:
$ npx playwright test --repeat-each 5 --reporter=line
Running 15 tests using 5 workers
[1/15] [chromium] › e2e/catalog.spec.ts:14:1 › adding a book updates the cart link
...
[15/15] [chromium] › e2e/catalog.spec.ts:24:1 › an out-of-stock book cannot be added
15 passed (3.8s)
Kullanıcı Gözüyle Locator'lar
Testlerde hiç CSS class'ı ya da XPath yok. Playwright'ın önerdiği sıra, kullanıcının sayfayı nasıl algıladığına göre: önce getByRole (rol ve accessible name), form alanlarında getByLabel, rolü olmayan metinlerde getByText, en son getByTestId. getByRole('button', { name: 'Search' }) bir düğmeyi ekran okuyucunun duyurduğu adla buluyor. Bu locator'ın kırılması için düğmenin metninin ya da rolünün değişmesi gerekiyor, yani kullanıcının gördüğü bir şeyin. Class adının btn-primary'den başka bir şeye dönmesi testi etkilemiyor.
Bu sıranın yan etkisi de faydalı: getByLabel('Search books') ancak input bir label'a bağlıysa çalışıyor. Label'ı unutulmuş bir form alanı önce testte görünüyor. Accessibility kontrolünün yerini tutmuyor ama erişilemeyen markup'ı test yazarken fark ettiriyor.
Locator'lar strict: bir action'ın hedefi tek bir element'e çözülmeli. Katalogda dört kart varken düğmeyi yalnız adıyla aramak şunu verdi:
Error: locator.click: Error: strict mode violation: getByRole('button', { name: 'Add to cart' }) resolved to 4 elements:
1) <button type="button" class="btn-primary"> Add to cart </button> aka getByRole('listitem').filter({ hasText: 'Dune₺64.907 in stock Add to' }).getByRole('button')
2) <button type="button" class="btn-primary"> Add to cart </button> aka getByRole('listitem').filter({ hasText: 'Emma₺39.503 in stock Add to' }).getByRole('button')
3) <button type="button" class="btn-primary"> Add to cart </button> aka getByRole('listitem').filter({ hasText: 'Middlemarch₺48.755 in stock' }).getByRole('button')
4) <button disabled type="button" class="btn-primary"> Add to cart </button> aka getByRole('listitem').filter({ hasText: 'The Hobbit₺52.00Out of stock' }).getByRole('button')
Hata, ilk eşleşeni tıklayıp yanlış kitabı sepete atmaktan çok daha iyi. Playwright her eşleşme için bir locator da öneriyor, ama önerilen hasText değerleri fiyat ve stok metnini de içeriyor; fiyat değiştiğinde kırılırlar. Testteki çözüm kartı kitabın başlığına göre daraltmak: filter({ has: page.getByRole('heading', { name: 'Emma' }) }), sonra kartın içinde düğmeyi aramak. Kısa yazmak istersen filter({ hasText: 'The Hobbit' }) de iş görüyor.
Test id'nin yeri, rol ve adın kullanıcıya bir şey söylemediği yerler: bir grafiğin çizim alanı, aynı metni taşıyan ve konumdan başka farkı olmayan element'ler, ya da metni A/B testi ve çeviriyle sık değişen bir bölüm. Playwright varsayılan olarak data-testid attribute'una bakıyor, config'de testIdAttribute ile değiştirilebiliyor. Bookstore'da şimdilik hiç kullanmadım; her test id, kullanıcının görmediği bir sözleşmeyi markup'a ekliyor.
Auto-wait ve Web-First Assertion'lar
Playwright bir action'dan önce element üzerinde actionability kontrolleri yapıyor: click için element'in görünür, sabit (animasyonu bitmiş), event alabilir (üstünü başka bir element örtmüyor), enabled ve 1.65'ten itibaren inert olmayan bir element olmasını bekliyor; fill için görünür, enabled ve editable olmasını. Bu yüzden testte "sayfa yüklensin" ya da "düğme aktif olsun" diye bir satır yok.
Bekleme action'la sınırlı. Tahmin bölümündeki hata, action'dan sonra durumu tek seferlik okuyan bir satırdan geldi. Bunu "biraz bekleyelim" diye çözmek cazip:
await page.getByRole('button', { name: 'Search' }).click();
await page.waitForTimeout(2000);
expect(await results.count()).toBe(1);
Bu test geçti ama 2,3 saniye sürdü; web-first assertion'lı sürüm aynı işi yaklaşık 350 milisaniyede yapıyor. Asıl sorun süre de değil: 2 saniye, CI runner'ı yavaşken ya da API soğuk başlarken yetmeyebilir; yettiği her koşuda da boşa harcanıyor. toHaveCount, toHaveText, toBeVisible, toBeDisabled, toHaveURL gibi assertion'lar koşulun gerçekleştiği anda dönüyor. Kural basit: expect'in içine bir değer değil, bir locator ya da page ver. await'li bir okuma (count(), textContent(), isVisible()) gördüğünde o satırı yeniden düşün.
Codegen ile Başlamak, Elle Düzeltmek
npx playwright codegen http://localhost:4200 bir tarayıcı ve yanında Playwright Inspector'ı açıyor; sayfada yaptığın her tıklama ve yazdığın her metin test koduna dönüşüyor. Locator seçerken codegen da aynı önceliği kullanıyor: rol, label, metin, test id. Inspector'daki "Pick locator" düğmesi, sayfada üzerine geldiğin element için Playwright'ın üreteceği locator'ı gösteriyor; mevcut bir teste satır eklerken en çok bu özelliği kullanıyorum. Visual Studio Code eklentisinde aynı kayıt editörün içinden yapılabiliyor.
Codegen'in ürettiği şey bir başlangıç, test değil. Kayıttan sonra yaptığım düzeltmeler hep aynı: page.goto'daki tam adresi baseURL'e göre göreli yola çevirmek, kaydın eklemediği ya da yalnız görünürlükle sınırlı bıraktığı assertion'ları testin asıl iddiasıyla değiştirmek, kayıt sırasında yapılan gereksiz tıklamaları silmek, rastgele kayıt verisini testin kendi verisiyle değiştirmek ve aynı kartı birden çok kez arayan satırları tek bir locator değişkenine toplamak.
Başarısız Testi Okumak: Trace Viewer, Screenshot ve UI Mode
Config'deki iki satır, kalan bir testin arkasında iz bırakıyor: trace: 'retain-on-failure' ve screenshot: 'only-on-failure'. Tahmin bölümündeki test kaldığında list reporter'ın çıktısı şöyle devam etti:
attachment #1: screenshot (image/png) ──────────────────────────────────────────────────────────
test-results/demo06-count-read-once-chromium/test-failed-1.png
────────────────────────────────────────────────────────────────────────────────────────────────
Error Context: test-results/demo06-count-read-once-chromium/error-context.md
attachment #3: trace (application/zip) ─────────────────────────────────────────────────────────
test-results/demo06-count-read-once-chromium/trace.zip
Usage:
npx playwright show-trace test-results/demo06-count-read-once-chromium/trace.zip
Trace dosyası 95 KB'tı. show-trace ile açılan trace viewer testi adım adım gösteriyor: solda action listesi, her action için öncesi ve sonrası DOM snapshot'ı (canlı bir sayfa gibi incelenebiliyor), action'ın hangi element'e uygulandığı, network sekmesinde API istekleri ve süreleri, console mesajları ve test kodunun ilgili satırı. Tahmin bölümündeki hatayı burada görmek bir dakika sürüyor: click action'ının "after" snapshot'ında liste hâlâ dört kitap, /api/books?search=dune isteği ise sayımdan sonra bitmiş.
error-context.md Playwright'ın son sürümlerinde eklenen bir dosya: hata mesajını ve sayfanın o anki accessibility ağacını YAML (insan tarafından okunması kolay bir veri formatı) olarak içeriyor. Bir AI asistanına "bu test neden kaldı?" diye sormak için hazırlanmış, ama bir insan için de ekran görüntüsünden çok daha hızlı okunuyor; ağaçta link "Cart (0)" gibi satırlar locator yazarken doğrudan işe yarıyor.
Trace modunu seçmek bir maliyet kararı. 'on' her testte trace kaydediyor ve suite'i yavaşlatıyor. CI için yaygın seçim 'on-first-retry': test ilk denemede kalır ve retry edilirse ikinci deneme kayıt altına alınıyor. Yerelde retry kapalı olduğu için 'retain-on-failure' daha kullanışlı; her test kaydediliyor ama yalnız kalanların dosyası saklanıyor. Video için de aynı seçenekler var (video: 'retain-on-failure'); trace zaten adım adım snapshot taşıdığı için videoyu ancak animasyon ya da zamanlamayla ilgili bir hatayı izlemek gerektiğinde açıyorum.
Test yazarken ise npx playwright test --ui ile açılan UI mode daha hızlı bir döngü veriyor: testleri listeden seçip çalıştırmak, dosya kaydedildiğinde yeniden çalıştıran watch modu, aynı trace görünümü ve sayfadan locator seçmek tek pencerede. --debug ise testi Inspector'da satır satır yürütüyor. CI'da kalan testin trace'i HTML raporuna ekleniyor; rapor artifact olarak yüklenince npx playwright show-report ile yerelde açılabiliyor.
Üretimde Dikkat
Playwright sürümünü sabitle ve tarayıcıları sürümle birlikte güncelle. @playwright/test'in her sürümü belirli tarayıcı build'leriyle test ediliyor; ^1.65.0 yazıp CI'da farklı bir minor sürüm çekmek, tarayıcı indirme adımını ve davranışı sessizce değiştiriyor. CI'da tarayıcıları npx playwright install --with-deps chromium ile kurmak ya da Playwright'ın resmî Docker image'ını kullanmak gerekiyor; GitHub Actions üzerinde bu adımlar GitHub Actions serisinin Angular CI yazısında.
Süreyi ölç. Bu üç test tek başına bir saniyenin altında bitiyor, ama suite'in toplam süresini uygulamanın başlaması belirliyor: SQL Server container'ı, migration, API ve ng serve'ün ilk derlemesi. Yerelde reuseExistingServer bu maliyeti bir kez ödetiyor; CI'da her koşu sıfırdan başlıyor. E2E testleri integration testlerinden pahalı olduğu için sayıları az, kapsadıkları akış önemli olmalı. Hangi akışın E2E'ye değdiği Test Stratejisi serisinin konusu.
Varsayılan zaman aşımlarını bil. Test başına 30 saniye, expect başına 5 saniye. CI'da yavaş bir ortam için zaman aşımlarını artırmak bazen gerekiyor, ama yerelde geçmeyen bir testi zaman aşımıyla düzeltmek, sorunu bir sonraki koşuya ertelemek demek.
Sık Düşülen Tuzaklar
CSS ve XPath selector'larına bağlanmak. page.locator('.book-card > button.btn-primary') bugün çalışıyor; tasarımcı düğmeyi bir div'e sardığında ya da class'ı yeniden adlandırdığında kalıyor, oysa kullanıcı için hiçbir şey değişmemiş oluyor. Daha kötüsü, .first() ile tek element'e indirilmiş bir CSS selector'ı, kartların sırası değiştiğinde yanlış kitaba tıklıyor ve test başka bir şeyi doğrulayarak geçebiliyor.
waitForTimeout ile beklemek. Yukarıdaki ölçümde iki saniyelik bekleme testi altı kat yavaşlattı ve yine de bir garanti vermiyor. Playwright dokümanı da bu method'u önerilmeyenler arasında sayıyor ve zamana göre bekleyen testlerin doğası gereği flaky olduğunu söylüyor. Beklemen gereken bir koşul varsa onu bir web-first assertion'la yaz; bekleyecek bir koşul bulamıyorsan test neyi doğruladığını henüz söylemiyor demektir.
Codegen çıktısını olduğu gibi commit'lemek. Kaydedilmiş bir test, kaydı yapan kişinin o günkü verisine, sayfadaki konumlara ve tıklama sırasına bağlı kalıyor; assertion'ları da çoğu zaman eksik. Kayıttan sonra yukarıdaki düzeltmeleri yapmadan pull request'e giren bir test, ilk veri değişikliğinde kalan ve kimsenin neyi test ettiğini anlamadığı bir test oluyor.
Yalnız Chromium'da test edip "tarayıcılar arası test edildi" demek. Bookstore'un config'inde bilerek tek proje var; Firefox ve WebKit eklemek koşu süresini katlıyor. Hangi tarayıcıları desteklediğin bir ürün kararı; eklediğin her proje o kararın test karşılığı olmalı.
Küçük Bir Deneme
★☆☆ Sepete ekleme testinin bir kopyasını getByRole('button', { name: 'Add to cart' }).nth(1) ile yaz ve yalnız "Cart (1)" bağlantısını kontrol et. Sonra host'taki seed verisine "Anna Karenina"yı ekle. İki testten hangisi kaldı, hangisi yanlış kitabı sepete atarak geçti?
★★☆ Tahmin bölümündeki testi trace ile çalıştır ve trace viewer'da /api/books?search=dune isteğinin count()'tan önce mi sonra mı bittiğini bul. Aynı testi toHaveCount ile yazıp iki trace'i karşılaştır.
★★★ C# test projesine sepete ekleme testini de taşı ve aynı locator'larla yaz. İki dildeki testin süresini ve kalan bir testte elde ettiğin bilgiyi (rapor, trace, ekran görüntüsü) karşılaştır; .NET tarafında trace'i yalnız kalan testler için saklamak için ne yazman gerekiyor?
Testler katalogda dolaşıyor ama veritabanını, API'yi ve Angular'ı ayağa kaldıran host'u kapalı bir kutu gibi kullandık. Bir sonraki yazıda o kutuyu açıyoruz: Testcontainers ile SQL Server, WebApplicationFactory.UseKestrel ile gerçek port üzerinde API ve webServer ile Angular; sonra kitap ara, sepete ekle, kupon uygula, sipariş ver akışını test verisini API'den hazırlayarak uçtan uca test ediyoruz.