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.
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
Windows kullanıyorsanız aktivasyon komutu şu şekilde olur:
.venv\Scripts\activate
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ı
Pytest testleri otomatik keşfeder. Şu kurallara uymanız yeterlidir:
- Test dosyaları
test_ile başlamalı veya_test.pyile bitmelidir. - Test fonksiyonları
test_ile başlamalıdır. - Test sınıfları
Testile başlamalı ve__init__içermemelidir.
Örnek pytest.ini:
[pytest]
testpaths = .
python_files = test_*.py *_test.py
python_functions = test_*
addopts = -v
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:
- HTTP isteği gönder.
- Yanıtı parse et.
- 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"
Testleri çalıştırın:
pytest -v
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"]
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"
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
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
Bu test şunları kontrol eder:
- Yanıtın başarılı oluşturma kodu dönmesi
-
idalanının integer olması -
quantitydeğerinin istekle uyumlu olması -
totaldeğ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)
Ş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
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
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")
Token için de aynı yaklaşımı kullanın:
AUTH_TOKEN = os.environ["API_AUTH_TOKEN"]
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
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
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
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
Alt küme çalıştırmak için:
pytest -m smoke
pytest -m "not slow"
Pratik bir CI stratejisi:
- Her commit'te
smoketestleri ç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.pyconfig.pyschemas/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.
-
parametrizeile veri odaklı testleri yerleşik destekler. - Mevcut
unittesttarzı 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
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
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}"},
)
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)