Makale 4: Test Otomasyon Frameworkleri: Basit Scriptlerden Production-Ready Çözümlere
Test Otomasyon Frameworkleri: Basit Scriptlerden Production-Ready Çözümlere
Playwright, Cypress, Selenium, Karate ve pytest için kapsamlı rehber: Apple ve diğer üst düzey şirketlerin tam olarak istediği
Buradasınız:
[✓] Makale 1: QA Temelleri
[✓] Makale 2: QA Pratiği
[✓] Makale 3: QA için DSA
[→] Makale 4: Otomasyon Frameworkleri ← Şu an okuyorsunuz
[ ] Makale 5: CI/CD
[ ] Makale 6: Performans Testi
[ ] Makale 7: Apple'da Çalışmak
İlerleme: 57% ✨
Apple Software Quality Engineer iş ilanını hatırlıyor musunuz? İşte ne istedikleri:
“Experience working with Automation frameworks such as: Karate, Playwright or Selenium”
Sadece “test çalıştırabiliyorum” değil, framework oluşturma. Bu makalede ele alacağımız konular:
- Production-ready otomasyon frameworkleri nasıl oluşturulur
- Playwright derinlemesine inceleme (modern standart)
- Selenium ileri düzey kalıplar (klasik)
- API testi için Karate (güçlü araç)
- Gerçek proje: Sıfırdan E-ticaret test framework’ü
Bu makalenin sonunda Apple, Google, Amazon mülakatçılarını etkileyecek hazır bir portfolyo projeniz olacak.
İçindekiler
- Test Otomasyonu Nedir?
- Bilmeniz Gereken Temel Terimler
- Test Otomasyonunun Evrimi
- Otomasyon Framework Mimarisi
- Playwright: Modern Standart
- Cypress: Developer-Friendly Testing
- Selenium WebDriver: İleri Düzey Kalıplar
- Karate: API Testing Powerhouse
- pytest: Python Test Frameworkü
- Best Practices & Anti-Patterns
- Öğrenme Kaynakları
Test Otomasyonu Nedir?
Problem: Manuel Test Yavaştır
Bir e-ticaret şirketinde test uzmanı olduğunuzu düşünün. Her gün şunları doğrulamanız gerekiyor:
- ✅ Kullanıcılar kayıt olabiliyor
- ✅ Kullanıcılar giriş yapabiliyor
- ✅ Ürünler doğru görüntüleniyor
- ✅ Alışveriş sepeti çalışıyor
- ✅ Ödeme işlemi çalışıyor
- ✅ Ödeme gerçekleşiyor
Manuel olarak bu her gün 2+ saat sürüyor. Ve bunu yapmanız gerekiyor:
- Her sürümden önce
- Farklı tarayıcılarda (Chrome, Firefox, Safari)
- Farklı cihazlarda (masaüstü, mobil, tablet)
Bu her gün 6+ saat tekrarlayan tıklama demek! 😱
Çözüm: Robotların Yapmasına İzin Verin
Test Otomasyonu şunları yapan programlar yazmak demektir:
- Tarayıcıyı otomatik olarak açar
- Butonlara tıklar, formları doldurur, sayfalarda gezinir
- Her şeyin doğru çalıştığını kontrol eder
- Sorunları raporlar
Basit analoji:
Her gün manuel olarak bulaşık yıkamak yerine bulaşık makinesi alırsınız. Yine de bulaşıkları yerleştirmeniz ve düğmelere basmanız gerekir, ama zor işi makine yapar.
Karşılaştırma:
| Manuel Test | Otomatik Test |
|---|---|
| Test başına 2 saat | Test başına 5 dakika |
| İnsan yorulur, hata yapar | Robot asla yorulmaz |
| Aynı anda 1 tarayıcıda test | Aynı anda 5 tarayıcıda test |
| Pahalı (insan maaşı) | Ucuz (sunucularda çalışır) |
| Sıkıcı ve tekrarlayıcı | Bir kez yaz, sonsuza kadar çalıştır |
Framework Nedir?
Framework testleriniz için organize bir yapıdır. Bir alet çantası gibi düşünün:
- Framework olmadan: Aletler her yerde dağınık, ihtiyacınız olanı bulmak zor
- Framework ile: Her şey etiketli çekmecelerde organize
Framework sağlar:
- 📁 Klasör yapısı (test dosyalarını nereye koyacağınız)
- 🔧 Yeniden kullanılabilir fonksiyonlar (kod tekrarlamayın)
- ⚙️ Yapılandırma (farklı ortamlar için ayarlar)
- 📊 Raporlama (ekran görüntüleriyle HTML raporlar)
- 🔄 CI/CD entegrasyonu (testleri otomatik çalıştırma)
Bilmeniz Gereken Temel Terimler
Framework’lere dalmadan önce temel terimleri anlayalım:
Tarayıcı ve DOM
| Terim | Nedir | Analoji |
|---|---|---|
| Tarayıcı | Chrome, Firefox, Safari — web sitelerini görüntüleme programları | İnternete bakmak için bir pencere |
| DOM | Document Object Model — bir web sayfasının yapısı | Bir web sayfasının iskeleti/planı |
| Element | Sayfanın herhangi bir parçası: buton, input, link, resim | İskeletteki bir kemik |
| Selector | Bir elementi bulmak için adres | Bir binayı bulmak için GPS koordinatları |
Test Terimleri
| Terim | Nedir | Örnek |
|---|---|---|
| Test Case | Doğrulanacak belirli bir şey | ”Kullanıcı doğru şifreyle giriş yapabilir” |
| Test Suite | İlgili test senaryolarının grubu | ”Tüm giriş testleri” |
| Assertion | Bir şeyin doğru olup olmadığını kontrol etme | ”Sayfa başlığı ‘Dashboard’ olmalı” |
| Fixture | Önceden hazırlanmış test verisi | Email’i “test@example.com” olan kullanıcı |
Selector Türleri
Otomasyona “BU butona tıkla” demeyi nasıl söyleriz? Selector’larla:
<button id="submit" class="btn primary" data-testid="login-btn">
Giriş Yap
</button>
| Selector Türü | Sözdizimi | Güvenilirlik |
|---|---|---|
| ID | #submit | ⭐⭐⭐ İyi |
| Class | .btn.primary | ⭐⭐ Orta |
| data-testid | [data-testid="login-btn"] | ⭐⭐⭐⭐ En İyi! |
| Text | text=Giriş Yap | ⭐⭐ Orta |
| XPath | //button[@id="submit"] | ⭐ Kırılgan |
En İyi Uygulama: Her zaman data-testid özniteliklerini kullanın — tasarım değiştiğinde değişmezler!
Test Otomasyonunun Evrimi
Tarih Neden Önemli
Buraya nasıl geldiğimizi anlamak şunlara yardımcı olur:
- Modern araçları takdir etmek
- İşteki eski kodu anlamak
- Daha iyi araç seçimleri yapmak
2010-2015: Selenium Dönemi (Başlangıç)
Problem: Testler ham komutlar olarak, birbiri ardına yazılıyordu.
// Eski yaklaşım - kırılgan, yavaş
// Her satır tarayıcıya ayrı bir komut
driver.findElement(By.id("username")).sendKeys("test@example.com");
// ^ id="username" olan elementi bul ve içine email yaz
driver.findElement(By.id("password")).sendKeys("password123");
// ^ Şifre alanını bul ve şifreyi yaz
driver.findElement(By.xpath("//button[@type='submit']")).click();
// ^ Gönder butonunu bul ve tıkla
Thread.sleep(5000); // 😱 Sabit bekleme!
// ^ 5 saniye bekle ve sayfanın yüklenmesini UMA
// Problem: Sayfa 1 saniyede yüklenirse? 4 saniye boşa gitti!
// Sayfa 6 saniye sürerse? Test başarısız olur!
Bu yaklaşımın sorunları:
- ❌ Sabit beklemeler — ne kadar bekleneceğini tahmin etmek
- ❌ Kırılgan selector’lar — HTML biraz değişirse XPath bozulur
- ❌ Yapı yok — sadece komut listesi
- ❌ Bakımı zor — her yerde kopyala-yapıştır
2016-2020: Page Object Model Dönemi
İyileştirme: Kodu ham komutlara göre değil, sayfalara göre organize etmek.
// Daha iyi, ama hala sorunlu
// Artık giriş sayfasını temsil eden bir sınıfımız var
class LoginPage {
constructor(driver) {
this.driver = driver;
}
async login(email, password) {
// Tüm giriş mantığı tek yerde
await this.driver.findElement(By.id("email")).sendKeys(email);
await this.driver.findElement(By.id("password")).sendKeys(password);
await this.driver.findElement(By.id("submit")).click();
}
}
// Testte kullanımı:
const loginPage = new LoginPage(driver);
await loginPage.login("test@example.com", "password123");
// Daha temiz! Ama hala zamanlama sorunları var...
2021-2026: Modern Otomasyon (Playwright Dönemi)
Devrim: Akıllı otomatik bekleme, basit sözdizimi, güvenilir testler.
// Modern yaklaşım - güvenilir, hızlı
// Playwright beklemeyi otomatik olarak yönetir!
test('geçerli kimlik bilgileriyle giriş', async ({ page }) => {
// test() - bir test senaryosu tanımlar
// async ({ page }) - Playwright bize bir tarayıcı sayfası verir
await page.goto('/login');
// ^ Giriş sayfasına git
// Playwright sayfanın yüklenmesini otomatik bekler!
await page.fill('[data-testid="email"]', 'test@example.com');
// ^ Email alanını bul ve içine yaz
// Playwright elementin görünür ve hazır olmasını bekler!
await page.fill('[data-testid="password"]', 'password123');
// ^ Şifreyi doldur
await page.click('[data-testid="login-button"]');
// ^ Giriş butonuna tıkla
// Playwright butonun tıklanabilir olmasını bekler!
await expect(page).toHaveURL(/dashboard/);
// ^ Assert: URL "dashboard" içermeli
// Playwright doğru olana kadar otomatik yeniden dener!
});
// Thread.sleep yok! Kırılgan test yok! 🎉
Playwright neden standart oldu:
| Avantaj | Açıklama |
|---|---|
| ✅ Otomatik bekleme | Artık sleep yok! |
| ✅ Çoklu tarayıcı | Chromium, Firefox, WebKit |
| ✅ Paralel çalıştırma | Kutudan çıktığı gibi |
| ✅ Ağ müdahalesi | API yanıtlarını taklit etme |
| ✅ Mükemmel dokümantasyon | Öğrenmesi kolay |
| ✅ Aktif destek | Microsoft |
Otomasyon Framework Mimarisi
Framework Mimarisi Nedir?
Bir ev inşa etmeyi düşünün:
- Plan olmadan → kaos, şeyler birbirine uymaz
- Plan ile → her odanın bir amacı var, her şey organize
Framework mimarisi test otomasyon projeniz için plandır.
Klasör Yapısı Açıklaması
automation-framework/
│
├── tests/ # 📋 TEST SENARYOLARI
│ │ # Tüm gerçek testleriniz burada
│ ├── e2e/ # Uçtan uca testler (tam kullanıcı yolculukları)
│ │ └── checkout.spec.js # Örnek: tam satın alma akışı
│ ├── api/ # API testleri (backend testi)
│ │ └── users.spec.js # Örnek: kullanıcı API endpoint'leri
│ └── integration/ # Entegrasyon testleri (parçaların birlikte çalışması)
│
├── pages/ # 📄 PAGE OBJECTS
│ │ # Her web sayfası = bir sınıf
│ ├── BasePage.js # Tüm sayfaların paylaştığı ortak işlevsellik
│ ├── LoginPage.js # Giriş sayfası: email, şifre, gönder
│ └── DashboardPage.js # Girişten sonra Dashboard
│
├── fixtures/ # 📊 TEST VERİLERİ
│ │ # Testler için önceden hazırlanmış veri
│ ├── users.json # {"email": "test@example.com", ...}
│ └── products.json # Alışveriş testleri için ürün verileri
│
├── utils/ # 🔧 YARDIMCI FONKSİYONLAR
│ │ # Yeniden kullanılabilir yardımcı kod
│ ├── database.js # Veritabanı bağlantıları
│ ├── dataGenerator.js # Rastgele test verisi oluştur
│ └── logger.js # Loglama fonksiyonları
│
├── config/ # ⚙️ YAPILANDIRMA
│ │ # Farklı ortamlar için farklı ayarlar
│ ├── dev.config.js # Geliştirme ortamı URL'leri
│ ├── staging.config.js # Staging ortamı URL'leri
│ └── prod.config.js # Production (burada dikkatli olun!)
│
├── reports/ # 📈 TEST RAPORLARI
│ # HTML raporlar, ekran görüntüleri, videolar
├── screenshots/ # 📸 Test başarısız olduğunda otomatik yakalanan
├── videos/ # 🎥 Testlerin video kayıtları
│
└── playwright.config.js # 🎯 ANA YAPILANDIRMA DOSYASI
# Tarayıcılar, zaman aşımları, paralelleştirme
Temel Prensipler (Örneklerle)
1. DRY (Don’t Repeat Yourself - Kendini Tekrarlama)
Problem: Birden fazla yerde aynı kod = bakım kabusu
// ❌ KÖTÜ - kod tekrarı
// Giriş formu değişirse, HER testi güncellemeniz gerekir!
test('test 1', async ({ page }) => {
await page.goto('/login');
await page.fill('#email', 'test@example.com');
await page.fill('#password', 'password');
await page.click('button[type="submit"]');
// ... testin geri kalanı
});
test('test 2', async ({ page }) => {
// Aynı giriş kodu tekrar!
await page.goto('/login');
await page.fill('#email', 'test@example.com');
await page.fill('#password', 'password');
await page.click('button[type="submit"]');
// ... testin geri kalanı
});
// 50 test düşünün... 50 yer güncellenecek! 😱
// ✅ İYİ - yeniden kullanılabilir page object
// Giriş mantığı TEK yerde
class LoginPage {
constructor(page) {
this.page = page;
}
async login(email, password) {
await this.page.goto('/login');
await this.page.fill('#email', email);
await this.page.fill('#password', password);
await this.page.click('button[type="submit"]');
}
}
// Artık testler temiz:
test('test 1', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.login('test@example.com', 'password');
// ... testin geri kalanı
});
test('test 2', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.login('test@example.com', 'password');
// ... testin geri kalanı
});
// Form değişti mi? LoginPage'i BİR KERE güncelle, tüm testler çalışır! 🎉
Playwright: Modern Standart
Playwright Nedir?
Playwright Microsoft’un şunları yapan bir aracıdır:
- Tarayıcıları (Chrome, Firefox, Safari) otomatik açar
- Butonlara tıklar, formları doldurur, sayfalarda gezinir
- Her şeyin doğru çalıştığını kontrol eder
- Ekran görüntüleri ve videolar alır
Basit analoji:
Playwright bilgisayar kullanabilen bir robot gibidir. Ona talimatlar verirsiniz: “bu web sitesine git, bu butona tıkla, bu metin görünüyor mu kontrol et” — ve herhangi bir insandan daha hızlı yapar.
Apple Neden Playwright İstiyor?
Gerçek iş ilanından:
“Experience with Playwright” — çünkü:
| Gereksinim | Ne anlama geliyor | Neden önemli |
|---|---|---|
| 🍎 WebKit desteği | Safari tarayıcısını test edebilir | Apple ürünleri Safari kullanır |
| ⚡ Paralel çalıştırma | Testleri aynı anda çalıştır | 100 test 50 dk yerine 5 dk’da |
| 🛡️ Otomatik bekleme | Manuel sleep yok | Testler rastgele başarısız olmaz |
| 🔧 Modern API | async/await sözdizimi | Temiz, okunabilir kod |
| 📱 Mobil emülasyonu | Mobil görünümleri test et | Uygulamalar telefonlarda çalışmalı |
Kurulum (Adım Adım)
Adım 1: Node.js’in kurulu olduğundan emin olun
# Node.js kurulu mu kontrol edin
node --version
# Şöyle bir şey göstermeli: v18.17.0
# Kurulu değilse, buradan indirin: https://nodejs.org/
Adım 2: Yeni proje oluşturun
# Klasör oluştur ve gir
mkdir my-automation-project
cd my-automation-project
# Playwright'ı başlat (istemleri takip edin)
npm init playwright@latest
Adım 3: Kurulum sorularını cevaplayın
✔ TypeScript veya JavaScript kullanmak istiyor musunuz? → JavaScript
✔ Uçtan uca testlerinizi nereye koymak istersiniz? → tests
✔ GitHub Actions workflow eklensin mi? → Evet
✔ Playwright tarayıcıları yüklensin mi? → Evet
# Bu Chrome, Firefox ve Safari tarayıcılarını yükler
Adım 4: Projeniz hazır!
# Örnek testleri çalıştır
npx playwright test
# Güzel HTML raporunu gör
npx playwright show-report
Playwright’ta Page Object Model
Page Object Model (POM) Nedir?
POM, her web sayfasının kendi sınıfını aldığı bir tasarım kalıbıdır. Bu sınıf şunları içerir:
- Locator’lar — sayfadaki elementleri nasıl bulacağınız
- Eylemler — sayfada neler yapabileceğiniz (tıkla, doldur, vb.)
- Assertion’lar — sayfanın doğru olduğunu nasıl doğrulayacağınız
pages/LoginPage.js:
// LoginPage.js
// Bu sınıf giriş sayfasını temsil eder
import { BasePage } from './BasePage';
export class LoginPage extends BasePage {
constructor(page) {
super(page); // Parent constructor'ı çağır
// Locator'ları tanımla — her elementi nasıl bulacağınız
// data-testid kullanmak en iyi uygulamadır!
this.emailInput = page.locator('[data-testid="email"]');
this.passwordInput = page.locator('[data-testid="password"]');
this.loginButton = page.locator('[data-testid="login-button"]');
this.errorMessage = page.locator('[data-testid="error-message"]');
}
async goto() {
// Giriş sayfasına git
await this.page.goto('/login');
await this.waitForPageLoad();
}
async login(email, password) {
// Email doldur
await this.emailInput.fill(email);
// Şifre doldur
await this.passwordInput.fill(password);
// Giriş butonuna tıkla
await this.loginButton.click();
}
async getErrorMessage() {
return await this.errorMessage.textContent();
}
}
Page Object Kullanan Testler
import { test, expect } from '@playwright/test';
import { LoginPage } from '../../pages/LoginPage';
test.describe('Giriş İşlevselliği', () => {
test('geçerli kimlik bilgileriyle başarılı giriş', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('test@example.com', 'SecurePass123!');
await expect(page).toHaveURL(/dashboard/);
});
test('geçersiz şifre ile giriş başarısız', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('test@example.com', 'yanlışşifre');
const errorMsg = await loginPage.getErrorMessage();
expect(errorMsg).toContain('Invalid credentials');
});
});
Cypress: Developer-Friendly Testing
Cypress Nedir?
Cypress doğrudan tarayıcıda çalışan JavaScript tabanlı bir test aracıdır. Tarayıcıyı dışarıdan kontrol eden Playwright/Selenium’dan farklı olarak, Cypress tarayıcının içinde çalışır.
Basit analoji:
Playwright bilgisayarınızı dışarıdan kumanda eden uzaktan kumandalı bir robot gibidir. Cypress bilgisayarda oturan, tam olarak sizin gördüğünüzü gören bir kişi gibidir.
Cypress Neden Popüler?
Cypress özellikle frontend geliştiriciler tarafından sevilir çünkü:
| Avantaj | Ne anlama geliyor | Neden harika |
|---|---|---|
| ✅ All-in-one | Her şey dahil | 10 paket yüklemeye gerek yok |
| ✅ Time Travel | Her adımı görsel olarak gör | Sayfa durumunu görmek için herhangi bir adıma tıkla |
| ✅ Real-time Reloads | Kod değişikliğinde otomatik yenile | Dosyayı kaydet → testler otomatik çalışır |
| ✅ Automatic Waiting | .sleep() gerekmiyor | Cypress elementleri otomatik bekler |
| ✅ Screenshots & Videos | Otomatik kaydedilir | Test başarısız olduğunda ne olduğunu gör |
Cypress vs Playwright: Hızlı Karşılaştırma
| Cypress | Playwright | |
|---|---|---|
| En iyi | JavaScript/React ekipleri için | Çapraz tarayıcı testi için |
| Tarayıcılar | Chrome, Firefox, Edge | Chrome, Firefox, Safari |
| Safari | ❌ Hayır | ✅ Evet |
| Birden fazla sekme | ❌ Hayır | ✅ Evet |
| Dil | Sadece JavaScript | JS, Python, Java, C# |
| Ücretsiz paralellik | ❌ Ücretli özellik | ✅ Ücretsiz |
| Hata ayıklama | ⭐⭐⭐⭐⭐ Mükemmel | ⭐⭐⭐⭐ Harika |
Cypress Kurulumu
# Yeni proje oluştur (gerekirse)
mkdir cypress-project
cd cypress-project
npm init -y
# Cypress'i yükle
npm install cypress --save-dev
# Cypress'i ilk kez aç
npx cypress open
Selenium WebDriver: İleri Düzey Kalıplar
Selenium Nedir?
Selenium tarayıcı otomasyonunun “dedesi”dir. 2004’te oluşturuldu ve özellikle mevcut test paketleri olan büyük şirketlerde hala yaygın olarak kullanılıyor.
Basit analoji:
Selenium klasik bir araba gibidir. Hala harika çalışır, milyonlarca insan kullanır ve onu nasıl tamir edeceğini bilen tamirciler her yerde var. Ama daha yeni arabaların (Playwright) daha iyi özellikleri var.
Selenium Ne Zaman Kullanılmalı?
| Selenium kullanın eğer: | Bunun yerine Playwright kullanın eğer: |
|---|---|
| ✅ Şirket zaten Selenium kullanıyor | ✅ Yeni proje başlıyorsunuz |
| ✅ Internet Explorer test etmeniz gerekiyor | ✅ Safari desteği istiyorsunuz |
| ✅ Ekip Java/Python/C# biliyor | ✅ Otomatik bekleme istiyorsunuz |
| ✅ Mobil için Appium kullanıyorsunuz | ✅ Daha kolay kurulum istiyorsunuz |
Karate: API Testing Powerhouse
Karate Nedir?
Karate API’leri (Application Programming Interface) test etmek için bir araçtır. Playwright kullanıcıların gördüklerini (butonlar, formlar) test ederken, Karate “arka planı” test eder — bir web sitesinin sunuculardan gönderdiği ve aldığı verileri.
Basit analoji:
Bir restoran düşünün. Playwright yemek salonunu test etmek gibidir (menü, masalar, garsonlar). Karate mutfağı test etmek gibidir (yemekler doğru hazırlanıyor mu? doğru malzemeler geliyor mu?).
API Nedir?
Karate’ye dalmadan önce API’nin ne olduğunu anlayalım:
API (Application Programming Interface) — programların birbiriyle iletişim kurma yoludur. Bir hava durumu uygulaması açtığınızda, sıcaklığı kendisi ölçmez — bir sunucuya sorar: “New York’ta hava nasıl?” ve bir yanıt alır.
Uygulamanız ──────────► Sunucu
"GET /weather?city=NYC"
Uygulamanız ◄────────── Sunucu
{"temp": 72, "condition": "sunny"}
Karate bu istek ve yanıtları doğrudan test eder, tarayıcı açmadan.
Gherkin Sözdizimi: Düz İngilizce’de Test Yazma
Karate Gherkin kullanır — neredeyse insan tarafından okunabilir dilde test yazmanın bir yolu:
Feature: Hava Durumu API Testi
Scenario: New York için hava durumu al
Given url 'https://api.weather.com' # Temel URL
And path 'weather' # Endpoint: /weather
And param city = 'NYC' # Query parametresi: ?city=NYC
When method GET # GET isteği gönder
Then status 200 # Başarı bekle (200 OK)
And match response.temp == '#number' # Sıcaklık bir sayı olmalı
HTTP Metodları Açıklaması
| Metod | Ne yapar | Örnek | Restoran Analojisi |
|---|---|---|---|
GET | Veri al | Kullanıcı profili al | ”Menüyü göster” |
POST | Yeni veri oluştur | Yeni kullanıcı oluştur | ”Bu yemeği sipariş etmek istiyorum” |
PUT | Tüm kaydı güncelle | Kullanıcı profilini güncelle | ”Tüm siparişimi değiştir” |
PATCH | Kısmi güncelleme | Sadece email değiştir | ”Sadece içeceği değiştir” |
DELETE | Veri sil | Hesabı sil | ”Siparişimi iptal et” |
HTTP Durum Kodları
| Kod | Ad | Anlam | Ne zaman görürsünüz |
|---|---|---|---|
200 | OK | Başarı | İstek çalıştı |
201 | Created | Başarıyla oluşturuldu | POST’tan sonra |
400 | Bad Request | Sizin hatanız | Yanlış format/veri |
401 | Unauthorized | Giriş yapmanız gerekiyor | Auth token eksik |
404 | Not Found | Mevcut değil | Yanlış URL/ID |
500 | Server Error | Sunucunun hatası | Sunucuda bug |
pytest: Python Test Frameworkü
pytest Nedir?
pytest Python için en popüler test framework’üdür. Playwright JavaScript içinse, pytest Python geliştiricileri içindir.
Basit analoji:
pytest bir test organizatörü gibidir. Testlerinizi bulur, çalıştırır ve neyin geçtiğini veya başarısız olduğunu söyler. Ödevinizi otomatik olarak notlandıran bir öğretmen gibi düşünün.
pytest Neden Kullanılır?
| Özellik | Ne anlama geliyor | Örnek |
|---|---|---|
| ✅ Basit Sözdizimi | Sadece fonksiyon yazın | def test_login(): |
| ✅ Fixture’lar | Testler için veri hazırlayın | Testten önce test kullanıcısı oluştur |
| ✅ Parametrizasyon | Aynı testi farklı verilerle çalıştır | Girişi 10 farklı kullanıcıyla test et |
| ✅ 800+ Plugin | İşlevselliği genişlet | HTML raporlar, paralel çalıştırma |
| ✅ Akıllı Assertion’lar | Sadece assert kullan | assert 2 + 2 == 4 |
Kurulum
# Temel pytest
pip install pytest
# Yaygın kullanışlı pluginler
pip install pytest-html # HTML raporlar
pip install pytest-xdist # Paralel çalıştırma
pip install pytest-cov # Kod kapsama
pip install pytest-playwright # Playwright entegrasyonu
İlk Testlerinizi Yazma
# Basit test fonksiyonları - sınıf gerekmiyor!
def test_toplama():
"""Toplamanın çalıştığını test et."""
assert 2 + 2 == 4
def test_cikarma():
"""Çıkarmanın çalıştığını test et."""
sonuc = 10 - 3
assert sonuc == 7, f"7 beklendi, {sonuc} alındı"
# ^ Özel hata mesajı
def test_liste_icerir():
"""Listenin beklenen öğeyi içerdiğini test et."""
meyveler = ["elma", "muz", "kiraz"]
assert "muz" in meyveler
Testleri çalıştır:
pytest # Tüm testleri çalıştır
pytest -v # Ayrıntılı çıktı
pytest tests/test_basic.py # Belirli dosyayı çalıştır
pytest -k "toplama" # Adında "toplama" olan testleri çalıştır
Best Practices & Anti-Patterns
En İyi Uygulamalar
1. data-testid özniteliklerini kullanın
<!-- ✅ İyi -->
<button data-testid="submit-button">Gönder</button>
<!-- ❌ Kötü - kırılgan seçici -->
<button class="btn btn-primary btn-lg">Gönder</button>
2. Sabit zaman aşımlarından kaçının
// ❌ Kötü
await page.waitForTimeout(5000);
// ✅ İyi
await page.waitForSelector('[data-testid="button"]', { state: 'visible' });
3. Testleri izole edin
// ✅ İyi - her test bağımsız
test.beforeEach(async ({ page }) => {
await page.goto('/');
await clearDatabase();
await seedTestData();
});
| ✅ Network Control | Ağ istekleri üzerinde tam kontrol | | ✅ Screenshots & Videos | Otomatik kayıt |
Cypress Kurulumu
npm install cypress --save-dev
npx cypress open
cypress.config.js:
const { defineConfig } = require('cypress');
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
viewportWidth: 1280,
viewportHeight: 720,
video: true,
screenshotOnRunFailure: true,
defaultCommandTimeout: 10000,
retries: {
runMode: 2,
openMode: 0
},
},
});
Cypress’te Page Object Model
cypress/support/pages/LoginPage.js:
class LoginPage {
get emailInput() {
return cy.get('[data-testid="email"]');
}
get passwordInput() {
return cy.get('[data-testid="password"]');
}
get loginButton() {
return cy.get('[data-testid="login-button"]');
}
visit() {
cy.visit('/login');
return this;
}
login(email, password) {
this.emailInput.type(email);
this.passwordInput.type(password);
this.loginButton.click();
return this;
}
}
export default new LoginPage();
Cypress ile Testler
import LoginPage from '../../support/pages/LoginPage';
describe('Giriş İşlevselliği', () => {
beforeEach(() => {
LoginPage.visit();
});
it('geçerli kimlik bilgileriyle başarılı giriş yapmalı', () => {
LoginPage.login('test@example.com', 'SecurePass123!');
cy.url().should('include', '/dashboard');
cy.get('[data-testid="welcome-message"]').should('be.visible');
});
it('geçersiz kimlik bilgileriyle hata göstermeli', () => {
LoginPage.login('test@example.com', 'yanlışşifre');
cy.get('[data-testid="error-message"]')
.should('be.visible')
.and('contain', 'Invalid credentials');
});
});
Custom Commands
cypress/support/commands.js:
// API üzerinden giriş (UI'dan hızlı)
Cypress.Commands.add('loginByAPI', (email, password) => {
cy.request({
method: 'POST',
url: '/api/auth/login',
body: { email, password }
}).then((response) => {
window.localStorage.setItem('authToken', response.body.token);
});
});
Network Mocking
describe('Network Mocking', () => {
it('API yanıtını taklit etmeli', () => {
cy.intercept('GET', '/api/users/profile', {
statusCode: 200,
body: {
id: 1,
name: 'Mock Kullanıcı',
premium: true
}
}).as('getProfile');
cy.visit('/profile');
cy.wait('@getProfile');
cy.get('[data-testid="premium-badge"]').should('be.visible');
});
});
Cypress vs Playwright: Karşılaştırma
| Kriter | Cypress | Playwright |
|---|---|---|
| Dil | JavaScript/TypeScript | JS/TS/Python/Java/C# |
| Tarayıcılar | Chrome, Firefox, Edge | Chromium, Firefox, WebKit |
| Hız | Hızlı | Çok Hızlı |
| Paralellik | Ücretli (Cypress Cloud) | Ücretsiz |
| Çoklu sekmeler | Desteklenmiyor | Destekleniyor |
| Debugging | Mükemmel Time Travel | Trace Viewer |
Selenium WebDriver: İleri Düzey Kalıplar
Selenium Ne Zaman Kullanılmalı?
| Selenium kullanın eğer: | Playwright kullanın eğer: |
|---|---|
| ✅ Legacy projeler zaten Selenium’da | ✅ Yeni proje |
| ✅ Eski tarayıcı desteği gerekli | ✅ Hız ve kararlılık gerekli |
| ✅ Appium ile entegrasyon (mobil) | ✅ Çoklu tarayıcı testi |
| ✅ Büyük eklenti ekosistemi | ✅ Modern web uygulamaları |
Karate: API Testing Powerhouse
Apple Neden Karate İstiyor?
İş ilanından:
“Experience with Karate” — çünkü:
- 📝 BDD sözdizimi (herkes tarafından anlaşılır)
- 🔥 API + Performans tek pakette
- ✅ Yerleşik assertion’lar
- ⚡ Paralel çalıştırma
Temel API Testi
Feature: Kullanıcı API Testi
Background:
* url 'https://api.example.com'
* header Accept = 'application/json'
Scenario: ID ile kullanıcı getir
Given path 'users', 1
When method GET
Then status 200
And match response.name == '#string'
Scenario: Yeni kullanıcı oluştur
Given path 'users'
And request { name: 'John Doe', email: 'john@example.com' }
When method POST
Then status 201
pytest: Python Test Frameworkü
Neden pytest?
pytest, Python ekosistemindeki en popüler test framework’üdür. Google, Dropbox ve Mozilla gibi şirketler tarafından kullanılır. Python tabanlı ortamlarda çalışan QA mühendisleri için vazgeçilmezdir.
Temel Avantajlar:
| Avantaj | Açıklama |
|---|---|
| ✅ Basit Sözdizimi | Boilerplate yok, sadece fonksiyonlar |
| ✅ Güçlü Fixtures | Test kurulumu için dependency injection |
| ✅ Parametrizasyon | Yerleşik data-driven testing |
| ✅ Zengin Plugin Ekosistemi | 800+ plugin mevcut |
| ✅ Detaylı Assertion’lar | Akıllı assertion analizi |
| ✅ Paralel Çalıştırma | pytest-xdist plugin’ı ile |
Kurulum
pip install pytest
pip install pytest-html pytest-xdist pytest-cov
pytest.ini:
[pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts = -v --tb=short --strict-markers
markers =
smoke: Hızlı smoke testleri
regression: Tam regression testleri
api: API testleri
ui: UI testleri
Proje Yapısı
pytest-framework/
├── tests/
│ ├── conftest.py # Paylaşılan fixtures
│ ├── test_login.py
│ ├── test_api.py
│ ├── api/
│ │ ├── conftest.py
│ │ └── test_users.py
│ └── ui/
│ ├── conftest.py
│ └── test_checkout.py
├── pages/ # Page Objects
│ ├── base_page.py
│ └── login_page.py
├── utils/
│ ├── api_client.py
│ └── data_generator.py
├── fixtures/
│ └── test_data.json
├── pytest.ini
└── requirements.txt
Test Yazma
tests/test_login.py:
import pytest
class TestLogin:
"""Giriş işlevi testleri."""
def test_successful_login(self, login_page, valid_user):
"""Kullanıcı geçerli kimlik bilgileriyle giriş yapabilir."""
login_page.login(valid_user.email, valid_user.password)
assert login_page.is_logged_in()
assert login_page.get_welcome_message() == f"Hoş geldiniz, {valid_user.name}"
def test_login_fails_with_invalid_password(self, login_page, valid_user):
"""Yanlış şifre ile giriş başarısız olur."""
login_page.login(valid_user.email, "wrongpassword")
assert login_page.get_error_message() == "Geçersiz kimlik bilgileri"
assert not login_page.is_logged_in()
@pytest.mark.parametrize("email,password,error", [
("", "password", "Email gereklidir"),
("test@example.com", "", "Şifre gereklidir"),
("invalid-email", "password", "Geçersiz email formatı"),
])
def test_login_validation(self, login_page, email, password, error):
"""Giriş formu hata mesajlarını doğrula."""
login_page.login(email, password)
assert login_page.get_error_message() == error
Fixtures (Dependency Injection)
tests/conftest.py:
import pytest
from dataclasses import dataclass
from playwright.sync_api import sync_playwright
@dataclass
class User:
email: str
password: str
name: str
@pytest.fixture(scope="session")
def browser():
"""Tüm test oturumu için tarayıcı örneği oluştur."""
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
yield browser
browser.close()
@pytest.fixture(scope="function")
def page(browser):
"""Her test için yeni sayfa oluştur."""
context = browser.new_context()
page = context.new_page()
yield page
context.close()
@pytest.fixture
def login_page(page):
"""LoginPage nesnesini başlat."""
from pages.login_page import LoginPage
return LoginPage(page)
@pytest.fixture
def valid_user():
"""Geçerli test kullanıcısı döndür."""
return User(
email="test@example.com",
password="SecurePass123!",
name="Test Kullanıcı"
)
@pytest.fixture
def api_client():
"""Kimlik doğrulamasıyla API istemcisi oluştur."""
from utils.api_client import APIClient
client = APIClient(base_url="https://api.example.com")
client.authenticate()
yield client
client.close()
pytest ile API Testi
tests/api/test_users.py:
import pytest
import requests
class TestUserAPI:
"""Kullanıcı API endpoint testleri."""
BASE_URL = "https://api.example.com"
def test_get_user_by_id(self, api_client):
"""GET /users/{id} kullanıcı detaylarını döndürür."""
response = api_client.get("/users/1")
assert response.status_code == 200
assert response.json()["id"] == 1
assert "email" in response.json()
def test_create_user(self, api_client):
"""POST /users yeni kullanıcı oluşturur."""
user_data = {
"name": "John Doe",
"email": "john.doe@example.com",
"password": "SecurePass123!"
}
response = api_client.post("/users", json=user_data)
assert response.status_code == 201
assert response.json()["name"] == user_data["name"]
assert "id" in response.json()
@pytest.mark.parametrize("user_id,expected_status", [
(1, 200),
(999, 404),
("invalid", 400),
])
def test_get_user_status_codes(self, api_client, user_id, expected_status):
"""Farklı kullanıcı ID'leri için doğru durum kodlarını doğrula."""
response = api_client.get(f"/users/{user_id}")
assert response.status_code == expected_status
İleri Düzey pytest Özellikleri
1. Özel Marker’lar
import pytest
@pytest.mark.smoke
def test_homepage_loads(page):
"""Ana sayfa için hızlı smoke testi."""
page.goto("/")
assert page.title() == "Home"
@pytest.mark.regression
@pytest.mark.slow
def test_complete_checkout_flow(page):
"""Tam ödeme akışı regression testi."""
# ... tam akış
pass
# Sadece smoke testlerini çalıştır:
# pytest -m smoke
# Yavaş testler hariç hepsini çalıştır:
# pytest -m "not slow"
2. Parametrize Edilmiş Fixtures
@pytest.fixture(params=["chrome", "firefox", "webkit"])
def browser_type(request):
"""Testleri birden fazla tarayıcıda çalıştır."""
return request.param
def test_cross_browser(browser_type, page):
"""Test tüm tarayıcılarda çalışır."""
page.goto("/")
assert page.title() == "Home"
3. Playwright ile pytest
import pytest
from playwright.sync_api import Page, expect
def test_login_with_playwright(page: Page):
"""pytest ile Playwright entegrasyonu."""
page.goto("/login")
page.fill('[data-testid="email"]', 'test@example.com')
page.fill('[data-testid="password"]', 'password123')
page.click('[data-testid="login-button"]')
expect(page).to_have_url("/dashboard")
expect(page.locator('[data-testid="welcome"]')).to_be_visible()
pytest Çalıştırma
# Tüm testleri çalıştır
pytest
# Ayrıntılı çıktı ile çalıştır
pytest -v
# Belirli test dosyasını çalıştır
pytest tests/test_login.py
# Belirli testi çalıştır
pytest tests/test_login.py::TestLogin::test_successful_login
# Anahtar kelimeyle eşleşen testleri çalıştır
pytest -k "login and not invalid"
# İşaretli testleri çalıştır
pytest -m smoke
# Paralel çalıştır (4 worker)
pytest -n 4
# HTML raporu oluştur
pytest --html=reports/report.html
# Coverage ile çalıştır
pytest --cov=src --cov-report=html
pytest vs Diğer Frameworkler
| Kriter | pytest | unittest | nose2 |
|---|---|---|---|
| Sözdizimi | Basit fonksiyonlar | Sınıf tabanlı | Her ikisi |
| Fixtures | Güçlü, esnek | setUp/tearDown | Sınırlı |
| Parametrizasyon | Yerleşik | Subtest | Plugin |
| Pluginler | 800+ | Sınırlı | Bazıları |
| Assertion’lar | Düz assert | self.assertEqual | Düz assert |
| Öğrenme Eğrisi | Kolay | Orta | Kolay |
| Topluluk | Çok büyük | Standart kütüphane | Azalıyor |
Best Practices & Anti-Patterns
En İyi Uygulamalar
1. data-testid özniteliklerini kullanın
<!-- ✅ İyi -->
<button data-testid="submit-button">Gönder</button>
<!-- ❌ Kötü - kırılgan seçici -->
<button class="btn btn-primary btn-lg">Gönder</button>
2. Sabit zaman aşımlarından kaçının
// ❌ Kötü
await page.waitForTimeout(5000);
// ✅ İyi
await page.waitForSelector('[data-testid="button"]', { state: 'visible' });
3. Testleri izole edin
// ✅ İyi - her test bağımsız
test.beforeEach(async ({ page }) => {
await page.goto('/');
await clearDatabase();
await seedTestData();
});
Öğrenme Kaynakları
Online Kurslar
- “Playwright: Web Automation Testing From Zero to Hero” — Udemy
- Test Automation University - Playwright — 🆓 Ücretsiz
- “Selenium WebDriver with Java” — Udemy
🛠️ Pratik Platformları
Mülakat Hazırlık Kontrol Listesi
Playwright:
- Page Object Model’i açıklayabiliyorum
- Fixture’ları nasıl kullanacağımı biliyorum
- Locator’ların nasıl çalıştığını anlıyorum
- CI/CD kurabiliyorum
- GitHub’da bir projem var
Selenium:
- Açık vs örtük beklemeleri biliyorum
- WebDriverWait ile çalışabiliyorum
Karate:
- API testleri yazabiliyorum
- Data-driven testing’i anlıyorum
pytest:
- Fixture scope ve dependency injection’u anlıyorum
- Data-driven testler için parametrizasyon kullanabiliyorum
- Özel marker’lar oluşturabiliyorum
- Playwright veya Selenium ile entegre edebiliyorum
- conftest.py organizasyonunu anlıyorum
- pytest-xdist ile paralel testler çalıştırabiliyorum
Sırada Ne Var?
Bir sonraki makalede QA Mühendisleri için CI/CD’yi ele alacağız:
- Test otomasyonu için Jenkins kurulumu
- GitHub Actions workflow’ları
- Testler için Docker
Sonraki Makale: Makale 5: QA için CI/CD
Yazar: AAnnayev — Senior SDET
Etiketler: #Playwright #Selenium #Karate #pytest #TestAutomation #Apple #SDET #QA