DEV Community

Cover image for Pytest API Otomatik Test Framework'ü: Uygulamalı Eğitim
Tobias Hoffmann
Tobias Hoffmann

Posted on • Originally published at apidog.com

Pytest API Otomatik Test Framework'ü: Uygulamalı Eğitim

Python geliştiricileri pytest'i tercih eder çünkü API testlerini az kalıp kodla yazmayı sağlar: test fonksiyonu test_ ile başlar, doğrulama için düz assert kullanılır ve çalıştırmayı pytest yönetir. requests ile birlikte kullandığınızda, API testleri için pratik, kod-öncelikli ve otomasyona uygun bir temel elde edersiniz.

Apidog'u bugün deneyin

Bu rehberde gerçekçi bir pytest API test paketi kuracağız. Projeyi hazırlayacak, ilk istek testini yazacak, ortak kurulumu fixture'lara taşıyacak, parametrize ile aynı testi farklı girdilerle çalıştıracak ve yanıt durumunu, gövdesini ve JSON Şemasını doğrulayacağız.

Projenin Kurulumu

Önce sanal ortam oluşturun ve gerekli paketleri yükleyin:

python -m venv .venv
source .venv/bin/activate
pip install pytest requests jsonschema
Enter fullscreen mode Exit fullscreen mode

Windows kullanıyorsanız aktivasyon komutu şu şekilde olur:

.venv\Scripts\activate
Enter fullscreen mode Exit fullscreen mode

Basit ve sürdürülebilir bir klasör yapısı kullanın:

api-tests/
  conftest.py        # ortak fixture'lar
  test_users.py      # users endpoint testleri
  test_orders.py     # orders endpoint testleri
  pytest.ini         # pytest yapılandırması
Enter fullscreen mode Exit fullscreen mode

Pytest testleri otomatik keşfeder. Şu kurallara uymanız yeterlidir:

  • Test dosyaları test_ ile başlamalı veya _test.py ile bitmelidir.
  • Test fonksiyonları test_ ile başlamalıdır.
  • Test sınıfları Test ile başlamalı ve __init__ içermemelidir.

Örnek pytest.ini:

[pytest]
testpaths = .
python_files = test_*.py *_test.py
python_functions = test_*
addopts = -v
Enter fullscreen mode Exit fullscreen mode

Otomatik test disiplini sizin için yeniyse, otomatik testin ne olduğu hakkındaki başlangıç rehberi iyi bir bağlam sağlar.

API testi için pytest kullanmanın pratik nedeni şudur: requests HTTP isteklerini yönetir, pytest ise test keşfi, okunabilir hata çıktısı, fixture tabanlı kurulum, veri odaklı testler ve raporlama tarafını çözer. Testler uygulama koduyla aynı depoda yaşadığında, API değişiklikleri ve bozulan testler aynı pull request içinde görünür hale gelir.

İlk API Testinizi Yazma

Bir pytest API testi genellikle üç adımdan oluşur:

  1. HTTP isteği gönder.
  2. Yanıtı parse et.
  3. Durum kodunu ve gövdeyi doğrula.

Örnek:

import requests

BASE_URL = "https://api.example.com/v1"

def test_get_user_returns_200():
    response = requests.get(f"{BASE_URL}/users/42")

    assert response.status_code == 200

def test_get_user_returns_expected_fields():
    response = requests.get(f"{BASE_URL}/users/42")
    body = response.json()

    assert body["id"] == 42
    assert "email" in body
    assert body["status"] == "active"
Enter fullscreen mode Exit fullscreen mode

Testleri çalıştırın:

pytest -v
Enter fullscreen mode Exit fullscreen mode

Başarısız bir assert, beklenen ve gerçek değerleri okunabilir şekilde gösterir. Özel assertion helper'ları yazmanız gerekmez; pytest düz assert ifadelerini zengin hata çıktısı üretecek şekilde işler.

API yanıtlarında kontrol etmeniz gereken alanlar hakkında daha fazla örnek için API doğrulamaları rehberine bakabilirsiniz.

Kurulumu Fixture'lar ile Paylaşma

Her testte BASE_URL, header veya HTTP session tekrar etmek sürdürülebilir değildir. Bu tür ortak kurulumu fixture'a taşıyın.

conftest.py dosyası, tüm test dosyaları tarafından otomatik kullanılabilir:

# conftest.py
import pytest
import requests

BASE_URL = "https://api.example.com/v1"

@pytest.fixture(scope="session")
def api_session():
    session = requests.Session()
    session.headers.update({"Accept": "application/json"})

    yield session

    session.close()

@pytest.fixture
def auth_token(api_session):
    response = api_session.post(
        f"{BASE_URL}/auth/login",
        json={
            "email": "qa@example.com",
            "password": "test-pass",
        },
    )

    assert response.status_code == 200

    return response.json()["token"]
Enter fullscreen mode Exit fullscreen mode

Burada:

  • scope="session" session nesnesinin tüm test çalıştırması boyunca bir kez oluşturulmasını sağlar.
  • yield öncesi kurulum, sonrası temizlik adımıdır.
  • auth_token, ihtiyaç duyan testlere token sağlar.

Test içinde fixture'ları parametre olarak çağırmanız yeterlidir:

def test_create_order(api_session, auth_token):
    response = api_session.post(
        f"{BASE_URL}/orders",
        headers={"Authorization": f"Bearer {auth_token}"},
        json={
            "product_id": 7,
            "quantity": 2,
        },
    )

    assert response.status_code == 201
    assert response.json()["status"] == "pending"
Enter fullscreen mode Exit fullscreen mode

Fixture'lar, setup_function ve teardown_function yaklaşımına göre daha esnektir. Bağımlılıkları açık hale getirir, farklı kapsamları destekler ve kolayca birleştirilebilir. Detaylar için resmi pytest fixture'lar dokümantasyonu incelenebilir.

Bir Testi Birçok Girdiyle Çalıştırma

API endpoint'leri genellikle farklı girdilerle test edilmelidir:

  • Geçerli değerler
  • Geçersiz değerler
  • Sınır değerler
  • Bulunamayan kaynaklar

Aynı test fonksiyonunu farklı girdilerle çalıştırmak için @pytest.mark.parametrize kullanın:

import pytest

BASE_URL = "https://api.example.com/v1"

@pytest.mark.parametrize(
    "user_id, expected_status",
    [
        (42, 200),
        (99999, 404),
        (0, 404),
        (-1, 400),
    ],
)
def test_get_user_status_codes(api_session, user_id, expected_status):
    response = api_session.get(f"{BASE_URL}/users/{user_id}")

    assert response.status_code == expected_status
Enter fullscreen mode Exit fullscreen mode

Bu tek fonksiyon dört ayrı test case üretir. Her biri bağımsız raporlanır; bir girdinin başarısız olması diğerlerini gizlemez.

Girdi seti büyüdüğünde test verisini CSV veya JSON dosyasından yüklemek daha okunabilir olabilir. Bu desen için CSV ve JSON ile veri odaklı API testi rehberine bakabilirsiniz.

Beklenen durum kodlarını seçerken tutarlı olmak için REST API'lerinin kullanması gereken HTTP durum kodları referansı da yararlıdır.

Yanıt Gövdesi ve Şeması Üzerine Doğrulama

Durum kodu tek başına yeterli değildir. 200 OK dönen ama eksik veya hatalı gövde içeren bir yanıt hâlâ hatadır.

Örnek gövde doğrulaması:

def test_order_response_shape(api_session, auth_token):
    response = api_session.post(
        f"{BASE_URL}/orders",
        headers={"Authorization": f"Bearer {auth_token}"},
        json={
            "product_id": 7,
            "quantity": 2,
        },
    )

    body = response.json()

    assert response.status_code == 201
    assert isinstance(body["id"], int)
    assert body["quantity"] == 2
    assert body["total"] > 0
    assert response.elapsed.total_seconds() < 1.0
Enter fullscreen mode Exit fullscreen mode

Bu test şunları kontrol eder:

  • Yanıtın başarılı oluşturma kodu dönmesi
  • id alanının integer olması
  • quantity değerinin istekle uyumlu olması
  • total değerinin pozitif olması
  • Yanıt süresinin belirlenen sınırın altında kalması

Daha güçlü yapısal kontrol için JSON Şeması kullanın:

from jsonschema import validate

order_schema = {
    "type": "object",
    "required": ["id", "product_id", "quantity", "status", "total"],
    "properties": {
        "id": {"type": "integer"},
        "product_id": {"type": "integer"},
        "quantity": {"type": "integer", "minimum": 1},
        "status": {"type": "string"},
        "total": {"type": "number"},
    },
}

def test_order_matches_schema(api_session, auth_token):
    response = api_session.post(
        f"{BASE_URL}/orders",
        headers={"Authorization": f"Bearer {auth_token}"},
        json={
            "product_id": 7,
            "quantity": 2,
        },
    )

    assert response.status_code == 201

    validate(instance=response.json(), schema=order_schema)
Enter fullscreen mode Exit fullscreen mode

Şema doğrulaması, tek tek alan kontrolüne göre daha iyi ölçeklenir. Eksik alan, yeniden adlandırılmış alan veya yanlış veri tipi gibi sözleşme bozulmalarını daha erken yakalar.

jsonschema kullanımı için doğrulama dokümantasyonu desteklenen anahtar kelimeleri açıklar.

Paketi CI'da Çalıştırma

Pytest başarısız testlerde sıfır olmayan çıkış kodu döndürür. Bu, CI sisteminin build'i başarısız sayması için yeterlidir.

JUnit raporu üretmek için:

pytest -v --junitxml=results.xml
Enter fullscreen mode Exit fullscreen mode

Bu komutu GitHub Actions, GitLab CI veya başka bir pipeline adımına ekleyebilirsiniz.

Örnek GitHub Actions adımı:

name: API Tests

on:
  push:
  pull_request:

jobs:
  api-tests:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install pytest requests jsonschema

      - name: Run API tests
        env:
          API_BASE_URL: https://staging.example.com/v1
        run: |
          pytest -v --junitxml=results.xml
Enter fullscreen mode Exit fullscreen mode

CI/CD kurulumu, secret enjeksiyonu ve ortam seçimi için CI/CD pipeline'larındaki API testleri rehberine bakabilirsiniz.

Ortam Değişkeni Kullanın

Token, parola veya ortam URL'sini test dosyalarına sabit kodlamayın. Bunun yerine ortam değişkeni okuyun:

import os

BASE_URL = os.environ.get("API_BASE_URL", "https://staging.example.com/v1")
Enter fullscreen mode Exit fullscreen mode

Token için de aynı yaklaşımı kullanın:

AUTH_TOKEN = os.environ["API_AUTH_TOKEN"]
Enter fullscreen mode Exit fullscreen mode

Bu sayede aynı test paketi:

  • Yerelde
  • Staging ortamında
  • CI pipeline içinde

kod değişikliği yapmadan çalışabilir.

Paralel Çalıştırmayı Düşünün

Testler bağımsızsa pytest-xdist ile paralel çalıştırabilirsiniz:

pip install pytest-xdist
pytest -n auto
Enter fullscreen mode Exit fullscreen mode

pytest -n auto, testleri CPU çekirdeklerine dağıtır. Ancak paralel çalışma için testlerinizin ortak duruma bağımlı olmaması gerekir. Çalıştırma sırasına bağlı testler paralel ortamda kararsız şekilde başarısız olur.

Bir pytest Paketini Sürdürülebilir Tutmak

Küçük bir test paketi kolay yönetilir. Paket büyüdükçe yapılandırma ve organizasyon önem kazanır.

Testleri Modüllere Ayırın

Endpoint veya domain bazlı dosyalar kullanın:

test_users.py
test_orders.py
test_payments.py
test_auth.py
Enter fullscreen mode Exit fullscreen mode

Sınıfları yalnızca gerçekten ortak kurulum paylaşıyorsanız kullanın. Sadece gruplama amacıyla sınıf eklemek genellikle gereksizdir.

Marker Kullanın

pytest.ini içinde marker'ları kaydedin:

[pytest]
markers =
    smoke: hızlı temel kontroller
    slow: uzun süren testler
Enter fullscreen mode Exit fullscreen mode

Testlerde kullanın:

import pytest

@pytest.mark.smoke
def test_get_user_returns_200(api_session):
    response = api_session.get(f"{BASE_URL}/users/42")

    assert response.status_code == 200
Enter fullscreen mode Exit fullscreen mode

Alt küme çalıştırmak için:

pytest -m smoke
pytest -m "not slow"
Enter fullscreen mode Exit fullscreen mode

Pratik bir CI stratejisi:

  • Her commit'te smoke testleri çalıştırın.
  • Gecelik pipeline'da tam paketi çalıştırın.

Yapılandırmayı Merkezileştirin

Temel URL'ler, ortak header'lar, şemalar ve fixture'lar tek yerde durmalıdır:

  • conftest.py
  • config.py
  • schemas/
  • helpers/

Aynı kodu ikinci kez yazıyorsanız, fixture veya yardımcı fonksiyona taşıyın. Bu modüler yaklaşım, otomatik test betikleri yazma rehberinde anlatılan disiplinle aynıdır.

Bunun Yerine Ne Zaman Bir Platforma Başvurmalı

Pytest, ekibiniz Python yazıyorsa ve testlerin uygulama koduyla aynı depoda yaşamasını istiyorsanız güçlü bir seçenektir.

Ancak şu durumlarda tek başına pytest daha fazla bakım gerektirebilir:

  • QA veya ürün ekibi kod yazmadan test katkısı yapmak istiyorsa
  • Test tasarımı, mocking ve yürütme tek yerde yönetilmek isteniyorsa
  • OpenAPI sözleşmesine göre otomatik şema doğrulaması merkezi bir ihtiyaçsa
  • Veri odaklı testleri arayüz üzerinden yönetmek daha pratikse

Apidog bu boşluğu kapatır. Görsel test oluşturma, OpenAPI spesifikasyonuna karşı şema doğrulaması, CSV ve JSON'dan veri odaklı çalıştırmalar ve CI için CLI çalıştırıcısı sunar; bunlar fixture ve doğrulama kodunu manuel yazmadan yapılabilir.

Birçok ekip iki yaklaşımı birlikte kullanır:

  • Mantık ağırlıklı, kodla sıkı bağlı senaryolar için pytest
  • Daha geniş kapsam, API tasarımı, mocking ve sözleşme doğrulaması için Apidog

Apidog'u indirebilir ve aynı endpoint üzerinde iki yaklaşımı karşılaştırabilirsiniz.

Sıkça Sorulan Sorular

API Testi İçin Python'ın Yerleşik unittest'i Yerine Neden pytest Kullanılmalı?

Pytest daha az kalıp kod gerektirir. Testler düz fonksiyonlardır, doğrulamalar düz assert ifadeleridir ve fixture'lar kurulumu unittest sınıf metotlarından daha esnek şekilde yönetir.

Ayrıca pytest:

  • Geniş eklenti ekosistemine sahiptir.
  • parametrize ile veri odaklı testleri yerleşik destekler.
  • Mevcut unittest tarzı testleri de çalıştırabilir.

Bu nedenle geçiş riski düşüktür.

Fixture ve Parametrize Arasındaki Fark Nedir?

Fixture, yeniden kullanılabilir kaynak sağlar:

  • HTTP session
  • Auth token
  • Test verisi
  • Geçici kaynak oluşturma/temizleme

parametrize ise aynı test gövdesini farklı girdilerle birden fazla kez çalıştırır.

Kısa fark:

  • Fixture kurulumu paylaşır.
  • Parametrize test senaryolarını çoğaltır.

Birlikte de kullanılabilirler:

@pytest.mark.parametrize("user_id", [1, 2, 3])
def test_get_user(api_session, user_id):
    response = api_session.get(f"{BASE_URL}/users/{user_id}")

    assert response.status_code == 200
Enter fullscreen mode Exit fullscreen mode

Pytest API Testlerinde Yanıt Süresini Doğrulamalı mıyım?

Evet, temel regresyonları yakalamak için kullanabilirsiniz:

assert response.elapsed.total_seconds() < 1.0
Enter fullscreen mode Exit fullscreen mode

Ancak pytest bir yük testi aracı değildir. Gerçek performans veya kapasite testi için özel yük testi araçları kullanılmalıdır.

Zamanlama doğrulamalarını çok sıkı yazmayın. Ağ değişkenliği, aksi halde kararsız testlere neden olabilir.

Pytest'te API Testlerini Nasıl Bağımsız Tutarım?

Her test kendi verisini oluşturmalı ve gerekiyorsa temizlemelidir. Bunu fixture'larla yapabilirsiniz:

@pytest.fixture
def created_order(api_session, auth_token):
    response = api_session.post(
        f"{BASE_URL}/orders",
        headers={"Authorization": f"Bearer {auth_token}"},
        json={"product_id": 7, "quantity": 2},
    )

    order = response.json()

    yield order

    api_session.delete(
        f"{BASE_URL}/orders/{order['id']}",
        headers={"Authorization": f"Bearer {auth_token}"},
    )
Enter fullscreen mode Exit fullscreen mode

Test yürütme sırasına güvenmeyin. Bağımsız testler paralel çalışabilir, izole başarısız olur ve hata ayıklaması daha kolaydır.

Pytest Yanıtları Bir OpenAPI Spesifikasyonuna Göre Doğrulayabilir mi?

Pytest bunu tek başına yapmaz. Ancak jsonschema ile JSON Şeması doğrulaması yapabilirsiniz ve OpenAPI belgesine göre yanıt kontrol eden eklentiler de mevcuttur.

Şema doğrulaması iş akışınızın merkezindeyse, OpenAPI spesifikasyonuna karşı otomatik doğrulama yapan Apidog gibi bir platform eklenti ve bakım yükünü azaltabilir.

Top comments (0)