İçeriğe atla

2025-12-20

Modern Web Uygulamaları için E2E Testing Stratejileri - Pratik Bir Mühendislik Rehberi

Playwright ve Cypress ile güvenilir, sürdürülebilir E2E test suite'leri nasıl oluşturulur öğrenin. Framework seçimi, flaky test önleme, CI/CD entegrasyonu ve gerçek dünya optimizasyon stratejilerini kapsıyor.

Özet

End-to-end testing, Playwright ve Cypress gibi modern framework’lerle önemli ölçüde gelişti. Bu rehber, gerçek bug’ları yakalarken flakiness’ı minimize eden güvenilir E2E test suite’leri oluşturmak için pratik stratejileri inceliyor. Framework seçimi, mimari pattern’ler, API mocking, visual regression, accessibility testing ve CI/CD optimizasyonunu kapsıyoruz. Bu araçlarla çalışırken öğrendiğim şey, başarının tool seçiminden çok mimari kararlardan geldiği; doğru test isolation, stable selector’ler ve dengeli test pyramid’leri hangi framework’ü seçtiğinden daha önemli.

Framework Seçimi: Playwright vs Cypress

Mimari Farklar

Playwright ve Cypress arasındaki seçim, birinin daha iyi olmasıyla ilgili değil; yetenekleri gereksinimlerle eşleştirmekle ilgili. Farklı senaryolarda neyin işe yaradığı:

Multi-browser + Native Parallelism

JavaScript-only + Hızlı Kurulum

E2E Testing Tool Seçimi

Proje Gereksinimleri

Playwright

Cypress

Güçlü Yönler

Native parallelism ücretsiz

Cross-browser WebKit/Firefox

Multi-language desteği

Auto-waiting mekanizması

Güçlü trace viewer

Güçlü Yönler

Interactive time-travel debugging

Real-time execution feedback

SPA'ler için mükemmel DX

Basit kurulum React/Vue/Angular

Sınırlamalar

Daha dik öğrenme eğrisi

Sık update maintenance

Sınırlamalar

Parallel execution için Cypress Cloud

Öncelikle Chrome odaklı

Sadece JavaScript/TypeScript

Çalışan Örnekler

İşte auto-waiting’i gösteren basit bir Playwright testi:

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

test('kullanici satin alma flow\'unu tamamlayabilir', async ({ page }) => {
  await page.goto('/products');

  // Element actionable olana kadar auto-wait yapar
  await page.getByTestId('product-add-to-cart').click();
  await page.getByTestId('checkout-button').click();

  // Checkout formunu doldur
  await page.getByTestId('shipping-name').fill('Ahmet Yilmaz');
  await page.getByTestId('shipping-address').fill('Ataturk Cad. No:123');
  await page.getByTestId('payment-card').fill('4242424242424242');

  await page.getByTestId('place-order').click();

  // Web-first assertion auto-retry yapar
  await expect(page.getByTestId('order-confirmation')).toBeVisible();
});

Aynı test Cypress’te:

describe('Satin Alma Flow', () => {
  it('kullanicinin satin alma tamamlamasina izin verir', () => {
    cy.visit('/products');

    cy.get('[data-testid="product-add-to-cart"]').click();
    cy.get('[data-testid="checkout-button"]').click();

    cy.get('[data-testid="shipping-name"]').type('Ahmet Yilmaz');
    cy.get('[data-testid="shipping-address"]').type('Ataturk Cad. No:123');
    cy.get('[data-testid="payment-card"]').type('4242424242424242');

    cy.get('[data-testid="place-order"]').click();

    cy.get('[data-testid="order-confirmation"]').should('be.visible');
  });
});

Her ikisi de aynı amaca ulaşıyor. Playwright’ın avantajı parallel execution’da ortaya çıkıyor; 8 shard ek maliyet olmadan eşzamanlı çalışıyor. Cypress aynı yetenek için Cypress Cloud subscription gerektiriyor.

Page Object Model ile Test Mimarisi

Page object’ler testleri UI yapısından ayırıyor. Bir button hareket ettiğinde veya class name değiştiğinde, onlarca test yerine bir dosyayı güncelliyorsun.

Modern Page Object Implementation

// page-objects/LoginPage.ts
import { Page, Locator, expect } from '@playwright/test';

export class LoginPage {
  readonly page: Page;
  readonly emailInput: Locator;
  readonly passwordInput: Locator;
  readonly submitButton: Locator;
  readonly errorMessage: Locator;

  constructor(page: Page) {
    this.page = page;
    this.emailInput = page.getByTestId('login-email-input');
    this.passwordInput = page.getByTestId('login-password-input');
    this.submitButton = page.getByTestId('login-submit-button');
    this.errorMessage = page.getByTestId('login-error-message');
  }

  async goto() {
    await this.page.goto('/login');
  }

  async login(email: string, password: string) {
    await this.emailInput.fill(email);
    await this.passwordInput.fill(password);
    await this.submitButton.click();
  }

  async expectLoginSuccess() {
    await expect(this.page).toHaveURL(/\/dashboard/);
  }

  async expectLoginError(message: string) {
    await expect(this.errorMessage).toContainText(message);
  }
}

Testlerde kullanımı:

test('gecerli credential\'lar login\'e izin verir', async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.goto();
  await loginPage.login('[email protected]', 'password123');
  await loginPage.expectLoginSuccess();
});

test('gecersiz credential\'lar hata gosterir', async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.goto();
  await loginPage.login('[email protected]', 'wrongpassword');
  await loginPage.expectLoginError('Gecersiz kimlik bilgileri');
});

Selector Stability

Test edeceğin elementler için data-testid attribute’ları kullan. Benim faydalı bulduğum naming convention: {scope}-{element}-{type}.

<!-- İyi: Stable, açıklayıcı test ID'ler -->
<button data-testid="product-list-add-to-cart-button">Sepete Ekle</button>
<input data-testid="checkout-shipping-name-input" />
<div data-testid="order-confirmation-message">Sipariş başarıyla verildi</div>

<!-- Kaçın: CSS class'lar refactor'larda değişir -->
<button class="btn btn-primary add-cart">Sepete Ekle</button>

Semantic HTML mevcut olduğunda, role-based locator’ları tercih et:

// Daha iyi: Accessible role kullanıyor
await page.getByRole('button', { name: 'Sepete Ekle' }).click();

// İyi: Explicit test ID
await page.getByTestId('add-to-cart-button').click();

// Kırılgan: Implementation-dependent
await page.locator('.product-card > .actions > button:nth-child(1)').click();

API Mocking Stratejileri

External API’leri mocklamak test isolation ve reliability sağlıyor. Yaklaşım rendering stratejine bağlı.

graph LR
    A[API Mocking Strategy] --> B{Rendering Type}
    B -->|Client-side only| C[Playwright page.route]
    B -->|Server-side SSR/SSG| D[MSW with Next.js proxy]
    B -->|Both CSR + SSR| E[MSW + Playwright integration]

    C --> F[Simple route mocking]
    F --> F1[Hızlı kurulum]
    F --> F2[Service worker overhead yok]

    D --> G[MSW browser mode]
    G --> G1[Vitest/Storybook\'ta tekrar kullanılabilir]
    G --> G2[Full Request/Response API]

    E --> H[Hybrid yaklasim]
    H --> H1[@msw/playwright package]
    H --> H2[window.msw pattern]

Playwright Native Mocking

Client-side app’ler için page.route() çoğu durumu hallediyor:

test('API fail olduğunda hata gösterir', async ({ page }) => {
  // API call'u intercept et ve error döndür
  await page.route('**/api/products', route => {
    route.fulfill({
      status: 500,
      contentType: 'application/json',
      body: JSON.stringify({ error: 'Internal Server Error' })
    });
  });

  await page.goto('/products');

  await expect(page.getByTestId('error-message'))
    .toContainText('Urunler yuklenemedi');
});

MSW ile Comprehensive Mocking

Mock Service Worker kompleks senaryolar için daha robust bir API sağlıyor:

// mocks/handlers.ts
import { http, HttpResponse } from 'msw';

export const handlers = [
  http.get('/api/products', () => {
    return HttpResponse.json([
      { id: 1, name: 'Urun 1', price: 299.99 },
      { id: 2, name: 'Urun 2', price: 399.99 }
    ]);
  }),

  http.post('/api/orders', async ({ request }) => {
    const body = await request.json();
    return HttpResponse.json(
      { orderId: '12345', status: 'confirmed' },
      { status: 201 }
    );
  })
];

Playwright ile entegrasyon:

import { setupWorker } from 'msw/browser';
import { handlers } from './mocks/handlers';

test.beforeEach(async ({ page }) => {
  // MSW worker'ı browser context'ine yükle
  await page.addInitScript(() => {
    const { setupWorker } = require('msw/browser');
    const { handlers } = require('./mocks/handlers');
    const worker = setupWorker(...handlers);
    worker.start();
  });
});

Gotcha: MSW’nin service worker’ı network request’leri page.route()’a görünmez yapıyor. Bir yaklaşımı tutarlı kullan veya @msw/playwright ile açıkça entegre et.

Flaky Test Önleme

Flaky test’ler güveni hiç test olmamaktan daha hızlı aşındırıyor. İşte onlara sebep olan şeyler ve nasıl düzeltilir:

Flaky Test Sebepleri

Timing Issues

External Dependencies

Test Isolation

Environment Inconsistency

Static wait\'ler await page.waitForTimeout

Missing element actionability check\'leri

Auto-waiting kullan

Web-first assertion\'lar kullan

Third-party API failure\'lari

Network latency

External call\'ları mockla

Real API\'ler için generous timeout\'lar

Shared test data

Test\'ler arası state leakage

Isolated database transaction\'lar

Düzgün beforeEach cleanup

Farklı OS rendering

Browser version mismatch\'leri

Containerized test execution

Browser version\'larını kilitle

Kaçınılacak Anti-pattern’ler

// BAD: Static wait'ler flakiness'a sebep olur
await page.click('#submit');
await page.waitForTimeout(3000); // Çok kısa veya çok uzun olabilir
await page.click('#next-step');

// Auto-waiting timing'i hallediyorù
await page.getByTestId('submit-button').click();
await expect(page.getByTestId('next-step-button')).toBeVisible();
// BAD: Unstable selector'ler UI değişiklikleriyle bozulur
await page.click('div.container > ul > li:nth-child(3) > button');

// Stable selector'ler refactoring'den kurtulur
await page.getByTestId('user-list-item-delete-button').click();

Retry Configuration

Retry’lar diagnostic tool’lar, çözüm değil. CI’da aralıklı infrastructure sorunlarını halletmek için kullan:

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  retries: process.env.CI ? 2 : 0, // Sadece CI'da retry
  use: {
    actionTimeout: 10000,
    navigationTimeout: 30000,
    trace: 'retain-on-failure', // Debug için kritik
    screenshot: 'only-on-failure',
    video: 'retain-on-failure'
  }
});

CI/CD Entegrasyonu ve Sharding

Parallel execution 35 dakikalık test suite’leri 5 dakikalık feedback loop’lara dönüştürüyor. GitHub Actions bunu basitleştiriyor:

# .github/workflows/e2e-tests.yml
name: E2E Tests
on: [push, pull_request]

jobs:
  playwright-tests:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        shardIndex: [1, 2, 3, 4, 5, 6, 7, 8]
        shardTotal: [8]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
        env:
          PLAYWRIGHT_BLOB_OUTPUT_DIR: blob-report
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: blob-report-${{ matrix.shardIndex }}
          path: blob-report
          retention-days: 1

  merge-reports:
    needs: playwright-tests
    if: always()
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
      - uses: actions/download-artifact@v4
        with:
          pattern: blob-report-*
          path: all-blob-reports
          merge-multiple: true
      - run: npx playwright merge-reports --reporter html ./all-blob-reports
      - uses: actions/upload-artifact@v4
        with:
          name: html-report
          path: playwright-report
          retention-days: 14

Performance impact: Yakın zamandaki bir projede, bu test execution’ı 35 dakikadan 5 dakikaya düşürdü; 7 kat iyileşme. Maliyet yaklaşık %14 arttı (8 concurrent runner vs. 1 sequential), bu da daha hızlı feedback için kolayca justify edildi.

Test Data Management

Temiz test data practice’leri testler arası müdahaleyi önlüyor ve reliability’yi artırıyor.

Factory Pattern

// test-data/factories.ts
import { Page } from '@playwright/test';

export class UserFactory {
  static async create(page: Page, overrides?: Partial<User>) {
    const userData = {
      email: `test-${Date.now()}@example.com`,
      name: 'Test Kullanici',
      role: 'member',
      ...overrides
    };

    // API üzerinden oluştur (UI'dan 10-50x daha hızlı)
    const response = await page.request.post('/api/users', {
      data: userData
    });

    return response.json();
  }

  static async cleanup(page: Page, userId: string) {
    await page.request.delete(`/api/users/${userId}`);
  }
}

// Testlerde kullanımı
test('kullanici profilini guncelleyebilir', async ({ page }) => {
  const user = await UserFactory.create(page);

  await page.goto(`/profile/${user.id}`);
  await page.getByTestId('profile-name').fill('Guncel Isim');
  await page.getByTestId('profile-save').click();

  await expect(page.getByTestId('profile-name')).toHaveValue('Guncel Isim');

  await UserFactory.cleanup(page, user.id);
});

Playwright Fixture’ları

Fixture’lar setup ve teardown’ı otomatik hallediyor:

// fixtures/index.ts
import { test as base } from '@playwright/test';

export const test = base.extend({
  authenticatedUser: async ({ page }, use) => {
    const user = await UserFactory.create(page, { role: 'user' });
    await loginAs(page, user);
    await use(user);
    await UserFactory.cleanup(page, user.id);
  },

  adminUser: async ({ page }, use) => {
    const admin = await UserFactory.create(page, { role: 'admin' });
    await loginAs(page, admin);
    await use(admin);
    await UserFactory.cleanup(page, admin.id);
  }
});

// Temiz test kodu
test('kullanici sepete urun ekleyebilir', async ({ authenticatedUser, page }) => {
  await page.goto('/products');
  await page.getByTestId('product-add-to-cart').first().click();
  await expect(page.getByTestId('cart-count')).toHaveText('1');
});

Visual Regression Testing

Visual regression’lar functional test’lerden geçiyor. Otomatik screenshot karşılaştırması onları yakalıyor.

Playwright Built-in Visual Testing

test('dashboard layout tutarli kaliyor', async ({ page }) => {
  await page.goto('/dashboard');

  // Dinamik içeriğin yüklenmesini bekle
  await page.waitForLoadState('networkidle');

  // Dinamik elementleri maskele
  await expect(page).toHaveScreenshot('dashboard.png', {
    mask: [
      page.getByTestId('user-greeting'), // Timestamp içeriyor
      page.getByTestId('notification-badge') // Dinamik sayı
    ],
    maxDiffPixels: 100
  });
});

Gotcha: Screenshot’lar OS-dependent. macOS’ta çekilen screenshot Linux’la match etmez. Tutarlılık için visual test’leri Docker container’larında çalıştır:

# Dockerfile.test
FROM mcr.microsoft.com/playwright:v1.47.0-jammy

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .

CMD ["npx", "playwright", "test"]

SaaS Alternatifleri

Docker kompleksitesi olmadan cross-platform tutarlılığa ihtiyaç duyan ekipler için:

  • Percy: AI-powered diff detection, cross-browser (fiyatlandırma ekip büyüklüğüne göre değişiyor; güncel fiyatları kontrol et)
  • Chromatic: Storybook entegrasyonu, visual approval workflow (fiyatlandırma snapshot sayısına göre değişiyor; güncel fiyatları kontrol et)
  • Lost Pixel (open-source): Percy’ye self-hosted alternatif

Trade-off: SaaS tool’lar paraya mal oluyor ama infrastructure management’ı ortadan kaldırıyor. Built-in çözümler ücretsiz ama containerization disiplini gerektiriyor.

Mobile Testing

Web trafiğinin yarısından fazlası mobil cihazlardan geliyor. Sadece desktop test etmek kritik sorunları kaçırıyor.

Device Emulation

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

// Önceden yapılandırılmış cihaz kullan
test.use(devices['iPhone 14 Pro']);

test('mobil navigasyon calisiyor', async ({ page }) => {
  await page.goto('/');

  // Touch event'ler otomatik etkin
  await page.getByTestId('mobile-menu-button').tap();
  await expect(page.getByTestId('mobile-nav')).toBeVisible();
});

// Birden fazla cihaz test et
const mobileDevices = ['iPhone 14 Pro', 'Pixel 5', 'Galaxy S24'];

for (const deviceName of mobileDevices) {
  test.describe(deviceName, () => {
    test.use(devices[deviceName]);

    test('checkout flow tamamlaniyor', async ({ page }) => {
      await page.goto('/checkout');
      // Test viewport'a adapte oluyor
    });
  });
}

Geolocation Testing

test.use({
  geolocation: { longitude: 29.0104, latitude: 41.0082 },
  permissions: ['geolocation']
});

test('konuma gore yakin magazalari gosteriyor', async ({ page }) => {
  await page.goto('/stores');

  await expect(page.getByTestId('store-location'))
    .toContainText('Istanbul');

  // Test ortasında konum değiştir
  await page.context().setGeolocation({
    longitude: 32.8597,
    latitude: 39.9334
  });

  await page.reload();

  await expect(page.getByTestId('store-location'))
    .toContainText('Ankara');
});

Accessibility Testing

Otomatik accessibility testing WCAG ihlallerinin %30-40’ını yakalıyor. Her test run’a entegre et.

import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';

test('anasayfa WCAG 2.1 AA standartlarini karsilıyor', async ({ page }) => {
  await page.goto('/');

  const results = await new AxeBuilder({ page })
    .withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
    .exclude('#third-party-widget') // Kontrol etmediğin external widget'lar
    .analyze();

  expect(results.violations).toEqual([]);
});

test('klavye navigasyonu uygulama boyunca calisiyor', async ({ page }) => {
  await page.goto('/');

  // İnteraktif elementler arasında tab ile gezin
  await page.keyboard.press('Tab');
  await expect(page.getByTestId('search-input')).toBeFocused();

  await page.keyboard.press('Tab');
  await expect(page.getByTestId('nav-link-about')).toBeFocused();

  await page.keyboard.press('Tab');
  await expect(page.getByTestId('nav-link-products')).toBeFocused();
});

Kademeli benimseme için, başlangıçta testleri fail etmeden violation’ları logla:

const results = await new AxeBuilder({ page }).analyze();

if (results.violations.length > 0) {
  console.warn(`[WARN] ${results.violations.length} accessibility violation bulundu:`);
  results.violations.forEach(violation => {
    console.warn(`  ${violation.id}: ${violation.description}`);
    console.warn(`  Etki: ${violation.impact}`);
    console.warn(`  Etkilenen elementler: ${violation.nodes.length}`);
  });
}

Component vs E2E Testing

Her şey E2E testing gerektirmiyor. Test pyramid hala geçerli.

Bireysel component logic

Multi-component interaction

Full user journey

Test Senaryosu

Neyi test ediyorsun?

Component Test

Integration Test

E2E Test

Avantajlar

Hızlı execution 100ms\'den az

Kolay debug isolated scope

External dependency yok

Kullanım Alanları

Form validation logic

Calculator\'larda edge case\'ler

Component state management

Avantajlar

Gerçek user flow\'ları test eder

Integration bug\'larını yakalar

Dezavantajlar

Yavaş execution 5-30s per test

Debug etmesi daha zor

Daha fazla maintenance overhead

Kullanım Alanları

Kritik user path\'lar login/checkout

Cross-service workflow\'lar

Third-party integration\'lar

Pratik Dağılım

  • %70 Unit/Component test’ler: Business logic, edge case’ler, hesaplamalar
  • %20 Integration test’ler: API + component interaction, multi-step workflow’lar
  • %10 E2E test’ler: Kritik user journey’ler (login, satın alma, kayıt)

Doğru seviyede test etme örneği:

// BAD: Edge case'leri E2E seviyesinde test etme
test('kupon kodu validasyonu: gecmis kuponlar', async ({ page }) => {
  await page.goto('/');
  await page.getByTestId('product-add').click();
  await page.getByTestId('checkout').click();
  await page.getByTestId('coupon-input').fill('EXPIRED2020');
  await page.getByTestId('coupon-apply').click();
  await expect(page.getByTestId('error')).toContainText('gecmis');
});

// Component seviyesinde test et
// tests/components/CouponValidator.test.ts
test('gecmis kupon kodlarini reddeder', () => {
  const validator = new CouponValidator();
  expect(validator.validate('EXPIRED2020')).toEqual({
    valid: false,
    error: 'Kupon gecmis'
  });
});

// E2E testler happy path'lere odaklanir
test('kullanici gecerli kuponla satin almayı tamamlar', async ({ page }) => {
  await page.goto('/');
  await page.getByTestId('product-add').click();
  await page.getByTestId('checkout').click();
  await page.getByTestId('coupon-input').fill('SAVE20');
  await page.getByTestId('coupon-apply').click();
  await expect(page.getByTestId('discount')).toContainText('20 TL');
  await page.getByTestId('complete-order').click();
  await expect(page.getByTestId('confirmation')).toBeVisible();
});

Yaygın Tuzaklar ve Çözümler

Tuzak 1: E2E Test’lere Aşırı Güven

Belirti: Test suite 30+ dakika sürüyor, çoğunlukla unit-level bug’ları yakalıyor.

Çözüm: Edge case’leri component test’lere taşı. E2E’yi kritik user path’lar için ayır.

Tuzak 2: Flaky Test’leri Görmezden Gelme

Belirti: “Tekrar çalıştır” kültürü güveni yok ediyor.

Çözüm: Flakiness metriklerini takip et. Flaky test’leri hemen karantinaya al veya düzelt. Flaky bir test suite hiç test olmamasından kötü.

Tuzak 3: Test Isolation Eksikliği

Belirti: Testler tek başına pass oluyor ama suite’te fail, sıraya bağımlı hatalar.

Çözüm: Her test izole çalıştırılabilir olmalı. Setup için factory’leri kullan, teardown’da temizle.

Tuzak 4: Trace Viewer Kullanmamak

Belirti: CI hatalarını local’de debug için saatler harcama.

Çözüm: Config’de trace: 'retain-on-failure' etkinleştir. CI artifact’larından trace dosyalarını indir ve npx playwright show-trace trace.zip ile aç. Viewer DOM snapshot’ları, network call’ları, console log’ları ve exact timing gösteriyor; saatler kazandırıyor.

Tuzak 5: Her Şeyi Mocklamak

Belirti: Tüm API call’lar mock’lanmış, testler pass ama production bozuk.

Çözüm: External third-party’leri ve error senaryolarını mockla. E2E testlerinde kendi API’ni mocklama; bu integration testing amacını bozuyor.

Önemli Çıkarımlar

  1. Framework seçimi mimariden daha az önemli: Page Object Model, stable selector’ler ve doğru test isolation hem Playwright hem Cypress’te çalışıyor.

  2. Hız için parallelize et: 8-way sharding execution’ı 35 dakikadan 5 dakikaya düşürdü; daha hızlı feedback için %14 maliyet artışına değer.

  3. Flakiness bir bug: Auto-waiting çoğu timing sorununu ortadan kaldırıyor. Flakiness metriklerini takip et ve agresif düzelt.

  4. Test pyramid’i dengele: %70 component, %20 integration, %10 E2E. Edge case’leri E2E seviyesinde test etme.

  5. Mobile testing opsiyonel değil: Device emulation mobil sorunların %95’ini kapsıyor. Viewport’ları, touch interaction’ları ve mobile performance’ı test et.

  6. Accessibility’yi otomatikleştir: axe-core entegrasyonu WCAG ihlallerinin %30-40’ını otomatik yakalıyor. Tam kapsam için manual testing hala gerekli.

  7. API-first test data: API üzerinden data oluşturmak UI navigation’dan 10-50x daha hızlı. Factory’leri ve fixture’ları kullan.

  8. Visual regression disiplin gerektirir: Docker container’ları cross-platform tutarlılığı sağlıyor. Dinamik içeriği maskele. Makul diff threshold’lar belirle.

  9. Debugging tool’larına yatırım yap: Trace viewer, screenshot’lar ve fail olan testler için video’lar kendilerini hızlıca geri ödüyor.

  10. Küçük başla, iterate et: 5-10 kritik path test ile başla. Coverage’ı genişletmeden önce değeri kanıtla.

E2E testing kapsamlı bir testing stratejisinde bir katman olarak ele alındığında en iyi sonucu veriyor. Kritik path’lerle başla, doğru mimariyle flakiness’ı önle ve parallelization ile scale et.

İlgili yazılar

Büyük Ölçekli Mikroservis Mimarisi için Ölçeklenebilir Bir GitHub Actions Platformu Oluşturmak

Organizasyon düzeyinde paylaşımlı bir GitHub Actions platformu kurmak için pratik bir rehber: mimari kararlar, güvenlik yönetişimi, benimseme stratejisi ve bu süreçte yaptığımız en büyük 7 hata.

github-actionsci-cddevops+5
Pact ile Contract Testing - Microservislerde API Uyumluluğunu Sağlama

TypeScript microservislerde consumer-driven contract testing'i Pact ile uygulamaya yönelik pratik bir kılavuz. Breaking API değişikliklerini deployment öncesi yakalayın ve integration test yükünü azaltın.

testingmicroservicesapi+7
Serverless Uygulamaları Test Etmek: Pratik Bir Strateji Rehberi

AWS Lambda, API Gateway, DynamoDB ve Step Functions için hızlı geri bildirim ve production güvenilirliği sağlayan kapsamlı bir test stratejisi oluşturmayı öğrenin.

lambdatestingserverless+11
LLM Kod İncelemesi: AI'ın İnsanların Kaçırdığını Yakaladığı Anlar

Gerçek kurumsal deneyimlere dayalı AI destekli kod incelemesi uygulama rehberi. AI'ın insanların kaçırdığını ne yakaladığını, insanların hala üstün olduğu alanları ve kod inceleme süreçlerinde etkili insan-AI işbirliği kurmayı öğrenin.

ai-code-reviewgithubsecurity+7
Git Branching Stratejileri: Farklı Takımlar ve Ürünler için Gerçek Dünya Dersleri

Takım büyüklüğü, ürün tipi ve gerçek başarısızlıklara dayanan Git branching stratejileri hakkında acımasızca dürüst bir rehber.

gitbranchingwar-stories+5