
Zelkulon17 Mart 20251 dk okuma
Playwright, en güçlü E2E test framework'üdür. Kurulumdan sağlam, sürdürülebilir testlere – Next.js örnekleri ve CI/CD entegrasyonu ile.
Playwright vs. Cypress
Playwright: Cypress: ────────────────────────────────── ────────────────────────────────── ✓ Chromium, Firefox, WebKit ✗ Öncelikli olarak Chromium ✓ Multi-Tab/Multi-Window ✗ Gerçek multi-tab yok ✓ Gerçek paralellik ✗ Yalnızca Enterprise planıyla ✓ Trace-Viewer (zaman yolculuğu debug) ✗ Yalnızca video/ekran görüntüsü ✓ TypeScript nativ ✓ Evet
Yapılandırma
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: [['html'], ['github']],
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'mobile', use: { ...devices['iPhone 14'] } },
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});Kararlı Locator'lar
// En iyi uygulama: rol tabanlı locator'lar (anlamsal, kararlı)
page.getByRole('button', { name: 'Absenden' });
page.getByRole('heading', { name: 'Kontakt' });
page.getByLabel('Deine E-Mail-Adresse');
page.getByPlaceholder('[email protected]');
page.getByTestId('submit-button'); // data-testid="..."
// Kabul edilebilir
page.getByText('Mehr erfahren');
// Kaçın: CSS sınıfları (refactoring'de bozulur)
page.locator('.btn-primary');
// Asla: XPath
page.locator('xpath=//div[3]/button[1]');Form Testi
test('iletişim formunu başarıyla gönderir', async ({ page }) => {
await page.goto('/de/contact');
await page.getByLabel('Dein Name').fill('Max Muster');
await page.getByLabel('E-Mail').fill('[email protected]');
await page.getByLabel('Nachricht').fill('Testnachricht.');
await page.getByRole('button', { name: 'Absenden' }).click();
await expect(page.getByText(/Danke für deine Nachricht/))
.toBeVisible({ timeout: 10_000 });
});
test('boş formda doğrulama hatalarını gösterir', async ({ page }) => {
await page.goto('/de/contact');
await page.getByRole('button', { name: 'Absenden' }).click();
await expect(page.getByText(/Name ist erforderlich/)).toBeVisible();
});API Mocking
test('API arızasında hata gösterir', async ({ page }) => {
// API hatasını simüle et
await page.route('**/api/blog/posts*', async route => {
await route.fulfill({ status: 500 });
});
await page.goto('/de/blog');
await expect(page.getByText(/Fehler beim Laden/)).toBeVisible();
});Page Object Model
// e2e/pages/BlogPage.ts
export class BlogPage {
constructor(private readonly page: Page) {}
async goto(locale = 'de') {
await this.page.goto(`/${locale}/blog`);
}
async search(query: string) {
await this.page.getByPlaceholder('Artikel suchen...').fill(query);
await this.page.keyboard.press('Enter');
}
async getArticleCount() {
return this.page.locator('[data-testid="blog-card"]').count();
}
}
// Testte kullanım
test('makale arar', async ({ page }) => {
const blogPage = new BlogPage(page);
await blogPage.goto();
await blogPage.search('Spring Boot');
expect(await blogPage.getArticleCount()).toBeGreaterThan(0);
});CI/CD – GitHub Actions
# .github/workflows/e2e.yml
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Run E2E tests
run: npx playwright test
env:
CI: true
- name: Upload test report
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/Özet
- Güvenilir: elemanlar için otomatik bekleme, kararsız testleri ortadan kaldırır
- Hızlı: gerçek paralellik, tarayıcı ek yükü yok
- Hata ayıklanabilir: Trace Viewer, hata ayıklamayı zaman yolculuğuna dönüştürür
- Kapsamlı: ekran görüntüsü testleri, API mocking, çok tarayıcı
- İpucu: yalnızca kritik kullanıcı yolculuklarını kapsa, her tıklamayı değil