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


Test Automation

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?

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:

  1. Tarayıcıyı otomatik olarak açar
  2. Butonlara tıklar, formları doldurur, sayfalarda gezinir
  3. Her şeyin doğru çalıştığını kontrol eder
  4. 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 TestOtomatik Test
Test başına 2 saatTest başına 5 dakika
İnsan yorulur, hata yaparRobot asla yorulmaz
Aynı anda 1 tarayıcıda testAynı 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

TerimNedirAnaloji
TarayıcıChrome, Firefox, Safari — web sitelerini görüntüleme programlarıİnternete bakmak için bir pencere
DOMDocument Object Model — bir web sayfasının yapısıBir web sayfasının iskeleti/planı
ElementSayfanın herhangi bir parçası: buton, input, link, resimİskeletteki bir kemik
SelectorBir elementi bulmak için adresBir binayı bulmak için GPS koordinatları

Test Terimleri

TerimNedirÖrnek
Test CaseDoğrulanacak belirli bir şey”Kullanıcı doğru şifreyle giriş yapabilir”
Test Suiteİlgili test senaryolarının grubu”Tüm giriş testleri”
AssertionBir şeyin doğru olup olmadığını kontrol etme”Sayfa başlığı ‘Dashboard’ olmalı”
FixtureÖnceden hazırlanmış test verisiEmail’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özdizimiGüvenilirlik
ID#submit⭐⭐⭐ İyi
Class.btn.primary⭐⭐ Orta
data-testid[data-testid="login-btn"]⭐⭐⭐⭐ En İyi!
Texttext=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:

AvantajAçıklama
✅ Otomatik beklemeArtık sleep yok!
✅ Çoklu tarayıcıChromium, Firefox, WebKit
✅ Paralel çalıştırmaKutudan çıktığı gibi
✅ Ağ müdahalesiAPI yanıtlarını taklit etme
✅ Mükemmel dokümantasyonÖğrenmesi kolay
✅ Aktif destekMicrosoft

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ü:

GereksinimNe anlama geliyorNeden önemli
🍎 WebKit desteğiSafari tarayıcısını test edebilirApple ürünleri Safari kullanır
⚡ Paralel çalıştırmaTestleri aynı anda çalıştır100 test 50 dk yerine 5 dk’da
🛡️ Otomatik beklemeManuel sleep yokTestler rastgele başarısız olmaz
🔧 Modern APIasync/await sözdizimiTemiz, okunabilir kod
📱 Mobil emülasyonuMobil görünümleri test etUygulamalar 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ü:

AvantajNe anlama geliyorNeden harika
✅ All-in-oneHer şey dahil10 paket yüklemeye gerek yok
✅ Time TravelHer adımı görsel olarak görSayfa durumunu görmek için herhangi bir adıma tıkla
✅ Real-time ReloadsKod değişikliğinde otomatik yenileDosyayı kaydet → testler otomatik çalışır
✅ Automatic Waiting.sleep() gerekmiyorCypress elementleri otomatik bekler
✅ Screenshots & VideosOtomatik kaydedilirTest başarısız olduğunda ne olduğunu gör

Cypress vs Playwright: Hızlı Karşılaştırma

CypressPlaywright
En iyiJavaScript/React ekipleri içinÇapraz tarayıcı testi için
TarayıcılarChrome, Firefox, EdgeChrome, Firefox, Safari
Safari❌ Hayır✅ Evet
Birden fazla sekme❌ Hayır✅ Evet
DilSadece JavaScriptJS, 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ı

MetodNe yaparÖrnekRestoran Analojisi
GETVeri alKullanıcı profili al”Menüyü göster”
POSTYeni veri oluşturYeni kullanıcı oluştur”Bu yemeği sipariş etmek istiyorum”
PUTTüm kaydı güncelleKullanıcı profilini güncelle”Tüm siparişimi değiştir”
PATCHKısmi güncellemeSadece email değiştir”Sadece içeceği değiştir”
DELETEVeri silHesabı sil”Siparişimi iptal et”

HTTP Durum Kodları

KodAdAnlamNe zaman görürsünüz
200OKBaşarıİstek çalıştı
201CreatedBaşarıyla oluşturulduPOST’tan sonra
400Bad RequestSizin hatanızYanlış format/veri
401UnauthorizedGiriş yapmanız gerekiyorAuth token eksik
404Not FoundMevcut değilYanlış URL/ID
500Server ErrorSunucunun 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?

ÖzellikNe anlama geliyorÖrnek
✅ Basit SözdizimiSadece fonksiyon yazındef test_login():
✅ Fixture’larTestler için veri hazırlayınTestten önce test kullanıcısı oluştur
✅ ParametrizasyonAynı testi farklı verilerle çalıştırGirişi 10 farklı kullanıcıyla test et
✅ 800+ Pluginİşlevselliği genişletHTML raporlar, paralel çalıştırma
✅ Akıllı Assertion’larSadece assert kullanassert 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

KriterCypressPlaywright
DilJavaScript/TypeScriptJS/TS/Python/Java/C#
TarayıcılarChrome, Firefox, EdgeChromium, Firefox, WebKit
HızHızlıÇok Hızlı
ParalellikÜcretli (Cypress Cloud)Ücretsiz
Çoklu sekmelerDesteklenmiyorDestekleniyor
DebuggingMükemmel Time TravelTrace 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:

AvantajAçıklama
✅ Basit SözdizimiBoilerplate yok, sadece fonksiyonlar
✅ Güçlü FixturesTest kurulumu için dependency injection
✅ ParametrizasyonYerleşik data-driven testing
✅ Zengin Plugin Ekosistemi800+ plugin mevcut
✅ Detaylı Assertion’larAkıllı assertion analizi
✅ Paralel Çalıştırmapytest-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

Kriterpytestunittestnose2
SözdizimiBasit fonksiyonlarSınıf tabanlıHer ikisi
FixturesGüçlü, esneksetUp/tearDownSınırlı
ParametrizasyonYerleşikSubtestPlugin
Pluginler800+SınırlıBazıları
Assertion’larDüz assertself.assertEqualDüz assert
Öğrenme EğrisiKolayOrtaKolay
ToplulukÇok büyükStandart kütüphaneAzalı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

  1. “Playwright: Web Automation Testing From Zero to Hero” — Udemy
  2. Test Automation University - Playwright — 🆓 Ücretsiz
  3. “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