Postman ile API Test Yazımı ve Otomasyon
Bir üretim ortamında beklenmedik bir API davranışıyla karşılaşmak, insan üzerinde kalıcı izler bırakıyor. Geçen yıl tam da böyle bir gecede, bir müşterinin ödeme sistemi sessiz sedasız yanlış tutarlar dönüyordu. Hatayı bulmak üç saat sürdü. Neden? Çünkü API uç noktaları için düzgün test senaryoları yoktu. İşte o geceden sonra Postman koleksiyonlarına olan bakış açım tamamen değişti.
Postman Neden Hala Geçerli?
Piyasada onlarca API test aracı var. insomnia, Bruno, HTTPie, hRESTClient… Bunların hepsini bir noktada denedim. Ama Postman, özellikle takım çalışması ve otomasyon söz konusu olduğunda hala güçlü bir konumda duruyor. Koleksiyon paylaşımı, ortam değişkenleri yönetimi ve özellikle Newman entegrasyonu sayesinde CI/CD pipeline’larına sorunsuz oturması, Postman’i sysadmin ve DevOps dünyasında vazgeçilmez kılıyor.
Bununla birlikte şunu da belirtmek lazım: Postman artık ağır bir araç. Electron tabanlı masaüstü uygulaması fazla ram yiyor, bazı özellikler bulut bağlantısı istiyor. Eğer hafif bir çözüm arıyorsanız Bruno veya Insomnia daha iyi hissettiriyor. Ama kolektif iş akışları ve pipeline entegrasyonu için Postman’in olgunluğuna henüz kimse yetişemedi.
Temel Kavramları Doğru Anlamak
Postman’e yeni başlayanların en sık yaptığı hata, her şeyi tek bir istek olarak bırakmak. Koleksiyon, klasör, istek hiyerarşisini düzgün kurmadan kalabalık bir workspace oluşup gidiyor ve sonra kimse o koleksiyonu kullanamıyor.
Koleksiyon: Birbirleriyle ilişkili API isteklerinin grubu. Bir servis için bir koleksiyon mantıklı bir başlangıç noktası.
Ortam (Environment): Değişken setleri. dev, staging, production gibi ortamlar için ayrı environment dosyaları oluşturup değişkenleri orada tutun.
Değişkenler: Koleksiyon seviyesinde, ortam seviyesinde ve global seviyede tanımlanabilir. Öncelik sırası: global < collection < environment < local.
Pre-request Script: İstek göndermeden önce çalışan JavaScript kodu. Token almak, timestamp üretmek, veri hazırlamak için kullanılır.
Tests: İstek gönderildikten sonra çalışan doğrulama scripti. Buradaki başarı/başarısızlık durumu Newman ile raporlanır.
İlk Koleksiyonu Kurmak
Örnek olarak bir e-ticaret API’si üzerinden gidelim. Ürün yönetimi, sipariş yönetimi ve kullanıcı işlemlerini kapsayan bir sistem düşünün.
Koleksiyon yapısı şu şekilde olmalı:
ECommerce API
├── Auth
│ ├── Login
│ └── Token Refresh
├── Products
│ ├── List Products
│ ├── Get Product
│ ├── Create Product
│ └── Delete Product
└── Orders
├── Create Order
├── Get Order
└── Update Order Status
Bu yapıyı Postman CLI üzerinden de oluşturabilirsiniz. Newman kurulumu için:
# Newman'ı global olarak kur
npm install -g newman
# Newman HTML reporter'ı da ekle
npm install -g newman-reporter-htmlextra
# Kurulumu doğrula
newman --version
Ortam Değişkenleri ve Kimlik Doğrulama
Her proje için en az iki ortam dosyası oluşturun: development ve staging. Production için ayrı, daha kısıtlı erişimli bir ortam olmalı ve bu ortam CI/CD secret’larından beslenmelidir.
Örnek bir environment JSON dosyası:
{
"name": "Development",
"values": [
{ "key": "baseUrl", "value": "http://localhost:3000/api/v1" },
{ "key": "username", "value": "[email protected]" },
{ "key": "password", "value": "testpassword123" },
{ "key": "authToken", "value": "" }
]
}
Auth token’ı Pre-request Script ile otomatik olarak yönetmek, tekrar eden login isteklerini ortadan kaldırır. Login koleksiyonunun Tests bölümüne şunu ekleyin:
// Login sonrası token'ı ortam değişkenine kaydet
if (pm.response.code === 200) {
const responseData = pm.response.json();
pm.environment.set("authToken", responseData.data.token);
pm.environment.set("tokenExpiry", responseData.data.expiresAt);
console.log("Token başarıyla güncellendi");
} else {
console.error("Login başarısız:", pm.response.text());
}
Koleksiyon seviyesinde Pre-request Script ile token yenileme mantığını merkeze taşıyabilirsiniz:
// Token süresi dolmuşsa yenile
const tokenExpiry = pm.environment.get("tokenExpiry");
const currentTime = new Date().getTime();
if (!tokenExpiry || currentTime > parseInt(tokenExpiry)) {
const loginRequest = {
url: pm.environment.get("baseUrl") + "/auth/login",
method: "POST",
header: { "Content-Type": "application/json" },
body: {
mode: "raw",
raw: JSON.stringify({
email: pm.environment.get("username"),
password: pm.environment.get("password")
})
}
};
pm.sendRequest(loginRequest, function(err, response) {
if (err) {
console.error("Token yenileme hatası:", err);
} else {
const data = response.json();
pm.environment.set("authToken", data.data.token);
pm.environment.set("tokenExpiry", data.data.expiresAt);
}
});
}
Gerçek Test Senaryoları Yazmak
Test yazmak “status code 200 mi?” diye kontrol etmekten ibaret değil. Gerçek dünyada işe yarayan testler response schema’sını, veri bütünlüğünü ve edge case’leri kapsamalı.
Ürün listeleme endpoint’i için kapsamlı bir test bloğu:
// Temel durum kodu kontrolü
pm.test("Status code 200 olmalı", function() {
pm.response.to.have.status(200);
});
// Response süresi kontrolü (SLA için kritik)
pm.test("Response süresi 500ms altında olmalı", function() {
pm.expect(pm.response.responseTime).to.be.below(500);
});
// Content-Type başlığı
pm.test("Content-Type JSON olmalı", function() {
pm.expect(pm.response.headers.get("Content-Type"))
.to.include("application/json");
});
// Schema doğrulama
pm.test("Response yapısı doğru olmalı", function() {
const responseData = pm.response.json();
pm.expect(responseData).to.be.an("object");
pm.expect(responseData).to.have.property("success", true);
pm.expect(responseData).to.have.property("data");
pm.expect(responseData.data).to.be.an("array");
});
// Veri bütünlüğü kontrolü
pm.test("Ürün nesneleri gerekli alanları içermeli", function() {
const products = pm.response.json().data;
if (products.length > 0) {
products.forEach(function(product) {
pm.expect(product).to.have.property("id");
pm.expect(product).to.have.property("name");
pm.expect(product).to.have.property("price");
pm.expect(product.price).to.be.a("number").and.to.be.above(0);
pm.expect(product).to.have.property("stock");
});
}
});
// Sayfalama parametreleri doğrulama
pm.test("Meta bilgileri mevcut olmalı", function() {
const meta = pm.response.json().meta;
pm.expect(meta).to.have.property("total");
pm.expect(meta).to.have.property("page");
pm.expect(meta).to.have.property("perPage");
});
// Sonraki istek için ilk ürün ID'sini kaydet
const products = pm.response.json().data;
if (products && products.length > 0) {
pm.environment.set("testProductId", products[0].id);
}
Negatif Test Senaryoları
Prodüksiyon hatalarının büyük çoğunluğu pozitif akışlarda değil, beklenmedik giriş verilerinde ortaya çıkıyor. Hata durumlarını test etmek en az başarılı akışları test etmek kadar önemli.
Yetkisiz erişim testi için token olmadan istek atıp beklenen 401 yanıtını doğrulayalım:
// Geçersiz token ile istek testi
pm.test("Geçersiz token 401 döndürmeli", function() {
pm.response.to.have.status(401);
});
pm.test("Hata mesajı Türkçe olmalı", function() {
const body = pm.response.json();
pm.expect(body).to.have.property("message");
pm.expect(body.message).to.be.a("string").and.to.not.be.empty;
});
pm.test("Hata response'u success false içermeli", function() {
pm.expect(pm.response.json().success).to.be.false;
});
Validation hatası senaryosu. Eksik alanlarla ürün oluşturmaya çalışın:
pm.test("Eksik alan 422 Unprocessable Entity döndürmeli", function() {
pm.response.to.have.status(422);
});
pm.test("Validation hataları field bazlı olmalı", function() {
const body = pm.response.json();
pm.expect(body).to.have.property("errors");
pm.expect(body.errors).to.be.an("object");
// 'name' alanı eksik gönderildiyse hata içermeli
pm.expect(body.errors).to.have.property("name");
});
Veri Akışı ile Uçtan Uca Test
API testinin gerçek gücü, isteklerin birbirini besleyebildiği akışlarda ortaya çıkıyor. Sipariş oluşturma akışı için bir senaryo:
- Kullanıcı login olur, token alır
- Ürün listesi çekilir, bir ürün ID’si alınır
- Bu ürünle sepet oluşturulur
- Sepet onaylanarak sipariş oluşturulur
- Sipariş ID’si ile durum sorgulanır
- Sipariş iptal edilir
Bu akışın ortam değişkenleri aracılığıyla nasıl bağlandığını göstermek için sipariş oluşturma testinin sonunu ele alalım:
// Sipariş başarıyla oluşturuldu mu?
pm.test("Sipariş oluşturma başarılı olmalı", function() {
pm.response.to.have.status(201);
});
pm.test("Sipariş ID'si döndürülmeli", function() {
const body = pm.response.json();
pm.expect(body.data).to.have.property("orderId");
pm.expect(body.data.orderId).to.be.a("string").and.to.not.be.empty;
// Sonraki testler için kaydet
pm.environment.set("testOrderId", body.data.orderId);
pm.environment.set("testOrderStatus", body.data.status);
});
pm.test("Başlangıç durumu pending olmalı", function() {
pm.expect(pm.response.json().data.status).to.equal("pending");
});
Newman ile CI/CD Entegrasyonu
Koleksiyonu komut satırından çalıştırmak test otomasyonunun temelidir. Temel Newman komutu:
newman run ./collections/ecommerce.postman_collection.json
--environment ./environments/staging.postman_environment.json
--reporters cli,htmlextra
--reporter-htmlextra-export ./reports/api-test-report.html
--iteration-count 1
--timeout-request 10000
--bail
Parametrelerin açıklaması:
–reporters cli,htmlextra: Terminal çıktısı ve HTML raporu üretir –reporter-htmlextra-export: HTML raporun kaydedileceği yol –iteration-count: Data-driven testing için kaç kez çalıştırılacağı –timeout-request: Milisaniye cinsinden request timeout süresi –bail: İlk hata sonrası koleksiyonu durdurur
GitLab CI pipeline örneği:
api-tests:
stage: test
image: node:18-alpine
before_script:
- npm install -g newman newman-reporter-htmlextra
script:
- newman run ./collections/ecommerce.postman_collection.json
--environment ./environments/staging.postman_environment.json
--env-var "baseUrl=${API_BASE_URL}"
--env-var "authToken=${API_TEST_TOKEN}"
--reporters cli,htmlextra
--reporter-htmlextra-export ./reports/api-test-${CI_PIPELINE_ID}.html
--timeout-request 15000
artifacts:
when: always
paths:
- reports/
expire_in: 7 days
only:
- staging
- main
GitHub Actions için benzer bir yapı:
name: API Tests
on:
push:
branches: [main, staging]
pull_request:
branches: [main]
jobs:
api-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Newman Kur
run: npm install -g newman newman-reporter-htmlextra
- name: API Testlerini Çalıştır
run: |
newman run ./collections/ecommerce.postman_collection.json
--environment ./environments/staging.postman_environment.json
--env-var "baseUrl=${{ secrets.API_BASE_URL }}"
--env-var "password=${{ secrets.TEST_USER_PASSWORD }}"
--reporters cli,htmlextra
--reporter-htmlextra-export ./reports/test-report.html
- name: Test Raporunu Sakla
uses: actions/upload-artifact@v3
if: always()
with:
name: api-test-report
path: reports/
Data-Driven Testing
Aynı endpoint’i farklı veri setleriyle test etmek için Newman’ın data dosyası özelliğini kullanabilirsiniz. CSV veya JSON formatında test verisi hazırlayın:
# data.json dosyası ile test
newman run ./collections/ecommerce.postman_collection.json
--environment ./environments/staging.postman_environment.json
--iteration-data ./test-data/products-data.json
--reporters cli,htmlextra
products-data.json dosyası şu yapıda olmalı:
[
{ "productName": "Laptop", "price": 25000, "stock": 10, "category": "electronics" },
{ "productName": "Klavye", "price": 1500, "stock": 50, "category": "accessories" },
{ "productName": "", "price": -100, "stock": 0, "category": "invalid" }
]
Son kayıt kasıtlı olarak hatalı veri içeriyor. Bu kaydın 422 döndürmesi bekleniyor ve bunu test bloğunda şöyle yakalarsınız:
// Data-driven negatif test
const productName = pm.iterationData.get("productName");
const price = pm.iterationData.get("price");
if (!productName || price < 0) {
pm.test("Geçersiz veri 422 döndürmeli", function() {
pm.response.to.have.status(422);
});
} else {
pm.test("Geçerli veri 201 döndürmeli", function() {
pm.response.to.have.status(201);
});
}
Pratik İpuçları ve Kaçınılması Gereken Hatalar
Koleksiyonlarınızı versiyon kontrolünde tutun. Export ile aldığınız JSON dosyasını Git reponuzda postman/ klasöründe saklayın. Her değişikliği commit’leyin. Bu sayede hangi testin ne zaman eklendiğini takip edebilirsiniz.
Ortam dosyalarındaki şifreleri ve token’ları asla versiyonlamayın. .gitignore‘a ekleyin, değerleri CI/CD secret’larından enjekte edin.
Test isimlerini anlamlı yazın. “Test 1”, “Check response” gibi isimler hata raporlarında hiçbir şey ifade etmiyor. “Ürün oluşturma – geçersiz fiyat 422 döndürmeli” gibi açıklayıcı isimler yazın.
Koleksiyon bazlı Pre-request Script’leri hata yakalamaya dikkat edin. Bir pre-request hatası tüm koleksiyonu durdurabilir. try-catch bloklarını ihmal etmeyin.
Her test izole çalışabilmeli ama akış testlerinde sıralamaya dikkat edin. Postman koleksiyon runner istekleri sırayla çalıştırır, bunu bilinçli kullanın.
Sonuç
Postman ile API testi yazmak aslında bir kültür meselesi. Araç öğrenmek bir günde olur ama bunu gerçek süreçlere entegre etmek, ekibin benimsemesini sağlamak ve sürdürülebilir bir test altyapısı kurmak farklı bir çaba gerektiriyor.
O ödeme sistemi olayından sonra koyduğumuz kural basitti: Bir API endpoint’i için kod yazılmadan önce test senaryoları Postman koleksiyonuna eklenecek. “Test-first” yaklaşımının API düzeyindeki uyarlaması. Bu kural başta sürtüşme yarattı, zaman kaybı gibi hissettirdi. Ama üç ay sonra aynı sistemde deployment sonrası bir regression’ı otomatik testler yakaladığında herkes ikna olmuştu.
Newman ile CI/CD entegrasyonu, deployment pipeline’ınıza entegre otomatik API testleri, data-driven senaryolar… Bunların hepsi kurulduktan sonra bakımı düşük, güvenilirliği yüksek bir test katmanı elde ediyorsunuz. Prodüksiyonda gece üç’te hata aramak yerine sabah deployment öncesi test raporuna bakmak çok daha insancıl bir iş akışı.
Başlangıç için küçük tutun. Kritik üç endpoint’i seçin, kapsamlı testler yazın, Newman ile pipeline’a bağlayın. Sonra genişletin. Sıfırdan mükemmel koleksiyon oluşturmaya çalışmak yerine çalışan küçük bir şeyle başlamak, sonunda daha iyi bir yere ulaştırıyor.
