API Mock Sunucusu Kurulumu: Geliştirme Ortamında Test
Geliştirme sürecinde en büyük baş ağrılarından biri, bağımlı olduğun bir API’nin henüz hazır olmaması ya da test sırasında gerçek servise istek atmak istememendir. Üçüncü taraf bir ödeme sistemi, henüz yazılmamış bir backend servisi ya da rate limit’i olan bir harici API… Bunların hepsi geliştiriciyi yavaşlatır. İşte bu noktada mock sunucuları devreye girer ve hayatı inanılmaz derecede kolaylaştırır.
Mock Sunucu Nedir ve Neden Lazım?
Mock sunucu, gerçek bir API’nin davranışını taklit eden sahte bir HTTP sunucusudur. Gerçek veritabanına dokunmaz, gerçek iş mantığı çalıştırmaz. Sadece “sen bana şunu sorarsın, ben sana bunu dönerim” mantığıyla çalışır.
Geliştirme ortamında mock sunucuya ihtiyaç duyulan tipik senaryolar şunlardır:
- Paralel geliştirme: Frontend ve backend ekipleri aynı anda çalışırken backend henüz hazır değil
- Harici API bağımlılıkları: Stripe, Twilio, SendGrid gibi servislere gerçek istek atmak istemiyorsun
- Hata senaryolarını test etme: Gerçek API’den 500 hatası almak için özel bir durum oluşturmak zor
- Offline geliştirme: İnternet bağlantısı olmadan çalışman gerekiyor
- Rate limit koruması: Ücretli bir API’yi geliştirme sırasında tüketmek istemiyorsun
- CI/CD pipeline: Otomatik testlerin harici servislere bağımlı olmaması gerekiyor
Bu yazıda json-server, WireMock ve Mockoon araçlarını kullanarak nasıl etkili bir mock ortamı kurulur, bunun üzerine gerçek dünya senaryoları koyacağım.
json-server ile Hızlı Başlangıç
En basit ve hızlı çözüm Node.js tabanlı json-serverdır. Bir JSON dosyası yazarsın, saniyeler içinde tam bir REST API ayağa kalkar.
Kurulum için önce Node.js’in kurulu olduğunu varsayıyorum:
npm install -g json-server
# Proje dizininde yerel olarak da kurabilirsin
npm install --save-dev json-server
Şimdi bir db.json dosyası oluşturalım. Diyelim ki bir e-ticaret uygulaması geliştiriyorsun:
cat > db.json << 'EOF'
{
"products": [
{
"id": 1,
"name": "Laptop Pro X",
"price": 45000,
"stock": 15,
"category": "electronics"
},
{
"id": 2,
"name": "Mekanik Klavye",
"price": 2800,
"stock": 50,
"category": "accessories"
},
{
"id": 3,
"name": "USB-C Hub",
"price": 850,
"stock": 0,
"category": "accessories"
}
],
"users": [
{
"id": 1,
"name": "Ahmet Yilmaz",
"email": "[email protected]",
"role": "admin"
},
{
"id": 2,
"name": "Fatma Kaya",
"email": "[email protected]",
"role": "user"
}
],
"orders": []
}
EOF
Sunucuyu başlatmak için:
# Varsayılan port 3000
json-server --watch db.json
# Farklı port ile
json-server --watch db.json --port 3001
# Sadece belirli bir host'tan erişim
json-server --watch db.json --host 0.0.0.0 --port 3001
Bu kadar. Artık şu endpoint’lerin hepsi çalışıyor:
GET /products– tüm ürünlerGET /products/1– ID’si 1 olan ürünPOST /products– yeni ürün eklePUT /products/1– ürünü güncelleDELETE /products/1– ürünü silGET /products?category=electronics– filtrelemeGET /products?_sort=price&_order=desc– sıralamaGET /products?_page=1&_limit=10– sayfalama
Test edelim:
# Ürünleri listele
curl -s http://localhost:3001/products | jq .
# Yeni ürün ekle
curl -s -X POST http://localhost:3001/products
-H "Content-Type: application/json"
-d '{"name":"Monitör 4K","price":12000,"stock":8,"category":"electronics"}' | jq .
# Filtreleme ile sorgu
curl -s "http://localhost:3001/products?category=accessories&_sort=price&_order=asc" | jq .
# Stokta olan ürünleri getir (stock_gte ile greater than equal)
curl -s "http://localhost:3001/products?stock_gte=1" | jq .
json-server’a Custom Route Eklemek
Gerçek API’ler genellikle /api/v1/products gibi prefix’ler kullanır. Bunu da ayarlayabiliriz:
# routes.json dosyası oluştur
cat > routes.json << 'EOF'
{
"/api/v1/*": "/$1",
"/api/v1/products/:id/details": "/products/:id"
}
EOF
json-server --watch db.json --routes routes.json --port 3001
Artık hem /products hem de /api/v1/products çalışıyor.
Middleware ile Kimlik Doğrulama Simülasyonu
Gerçek dünyada API’ler token doğrulaması yapar. json-server’a bunu ekleyebiliriz:
cat > middleware.js << 'EOF'
module.exports = (req, res, next) => {
// OPTIONS isteklerini doğrudan geç (CORS preflight)
if (req.method === 'OPTIONS') {
return next();
}
// Authorization header kontrolü
const authHeader = req.headers['authorization'];
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({
error: 'Unauthorized',
message: 'Authorization header gerekli'
});
}
const token = authHeader.split(' ')[1];
// Test için basit token kontrolü
if (token !== 'test-token-123') {
return res.status(403).json({
error: 'Forbidden',
message: 'Geçersiz token'
});
}
// CORS headers ekle
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
};
EOF
json-server --watch db.json --middlewares middleware.js --port 3001
Test edelim:
# Token olmadan istek (401 döner)
curl -s http://localhost:3001/products
# Yanlış token (403 döner)
curl -s -H "Authorization: Bearer yanlis-token" http://localhost:3001/products
# Doğru token ile istek
curl -s -H "Authorization: Bearer test-token-123" http://localhost:3001/products | jq .
WireMock ile İleri Seviye Mock Senaryoları
json-server basit CRUD senaryoları için mükemmel ama karmaşık response senaryoları, dinamik response’lar veya spesifik istek eşleştirme gerektiğinde WireMock daha güçlü bir araç.
WireMock’u Docker ile ayağa kaldırmak en pratik yol:
# WireMock'u Docker ile başlat
docker run -d
--name wiremock
-p 8080:8080
-v $(pwd)/wiremock-stubs:/home/wiremock/mappings
wiremock/wiremock:latest
--verbose
--global-response-templating
# Log'ları kontrol et
docker logs -f wiremock
Şimdi bir mapping dosyası oluşturalım. Diyelim ki ödeme sistemi entegrasyonu yapıyorsun ve Stripe benzeri bir API’yi mock’lamak istiyorsun:
mkdir -p wiremock-stubs
cat > wiremock-stubs/payment-success.json << 'EOF'
{
"mappings": [
{
"name": "Başarılı ödeme",
"request": {
"method": "POST",
"url": "/v1/charges",
"headers": {
"Authorization": {
"matches": "Bearer sk_test_.*"
},
"Content-Type": {
"contains": "application/json"
}
},
"bodyPatterns": [
{
"matchesJsonPath": "$.amount[?(@ > 0)]"
}
]
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"jsonBody": {
"id": "ch_mock_{{randomValue type='ALPHANUMERIC' length=24}}",
"object": "charge",
"amount": "{{jsonPath request.body '$.amount'}}",
"currency": "try",
"status": "succeeded",
"created": "{{now epochOffset='0' unit='seconds'}}",
"livemode": false
}
}
},
{
"name": "Yetersiz bakiye hatası",
"priority": 2,
"request": {
"method": "POST",
"url": "/v1/charges",
"bodyPatterns": [
{
"matchesJsonPath": "$.amount[?(@ > 100000)]"
}
]
},
"response": {
"status": 402,
"headers": {
"Content-Type": "application/json"
},
"jsonBody": {
"error": {
"type": "card_error",
"code": "insufficient_funds",
"message": "Kartınızda yeterli bakiye bulunmamaktadır.",
"decline_code": "insufficient_funds"
}
}
}
}
]
}
EOF
# WireMock'u yeniden yükle
curl -s -X POST http://localhost:8080/__admin/mappings/reset
Test edelim:
# Başarılı ödeme (5000 kuruş = 50 TL)
curl -s -X POST http://localhost:8080/v1/charges
-H "Authorization: Bearer sk_test_abcdef123456"
-H "Content-Type: application/json"
-d '{"amount": 5000, "currency": "try", "source": "tok_visa"}' | jq .
# Yetersiz bakiye hatası (100001 kuruş > 100000)
curl -s -X POST http://localhost:8080/v1/charges
-H "Authorization: Bearer sk_test_abcdef123456"
-H "Content-Type: application/json"
-d '{"amount": 150000, "currency": "try", "source": "tok_visa"}' | jq .
WireMock ile Gecikme Simülasyonu
Gerçek servislerin yavaş olduğu durumları test etmek için:
cat > wiremock-stubs/slow-response.json << 'EOF'
{
"mappings": [
{
"name": "Yavaş SMS servisi",
"request": {
"method": "POST",
"url": "/api/sms/send"
},
"response": {
"status": 200,
"fixedDelayMilliseconds": 3000,
"jsonBody": {
"message_id": "sms_mock_001",
"status": "queued"
}
}
},
{
"name": "Rastgele gecikmeli response",
"request": {
"method": "GET",
"urlPattern": "/api/report/.*"
},
"response": {
"status": 200,
"delayDistribution": {
"type": "lognormal",
"median": 1000,
"sigma": 0.5
},
"jsonBody": {
"status": "completed",
"data": []
}
}
}
]
}
EOF
Mockoon ile GUI Tabanlı Mock Sunucu
Bazı ekiplerde terminal kullanmayan üyeler de olabilir. Mockoon tam bu ihtiyaç için: hem GUI hem de CLI desteği olan cross-platform bir araç.
CLI versiyonunu kur:
npm install -g @mockoon/cli
# Veya Docker ile
docker pull mockoon/cli:latest
Mockoon için ortam dosyası oluşturalım. Bu dosya Mockoon GUI ile oluşturulan JSON formatında:
cat > mockoon-env.json << 'EOF'
{
"uuid": "mock-env-001",
"lastMigration": 29,
"name": "E-ticaret Mock API",
"endpointPrefix": "api",
"latency": 0,
"port": 3002,
"hostname": "0.0.0.0",
"routes": [
{
"uuid": "route-001",
"documentation": "Ürün listesi",
"method": "get",
"endpoint": "products",
"responses": [
{
"uuid": "resp-001",
"body": "[{"id":1,"name":"Test Ürün","price":100}]",
"latency": 0,
"statusCode": 200,
"label": "Başarılı",
"headers": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"bodyType": "INLINE",
"default": true
}
],
"enabled": true,
"responseMode": null
}
],
"enabled": true,
"tlsOptions": {
"enabled": false
},
"cors": true,
"headers": [],
"proxyMode": false,
"proxyHost": "",
"proxyRemovePrefix": false,
"proxyReqHeaders": [],
"proxyResHeaders": [],
"data": [],
"folders": [],
"rootChildren": []
}
EOF
# Mockoon CLI ile başlat
mockoon-cli start --data mockoon-env.json --port 3002
# Docker ile
docker run -d
--name mockoon
-p 3002:3002
-v $(pwd)/mockoon-env.json:/data/mockoon-env.json
mockoon/cli:latest
start --data /data/mockoon-env.json
Docker Compose ile Komple Mock Ortamı
Gerçek dünyada tek bir mock sunucu yetmez. Frontend, backend, farklı harici servisler için ayrı mock’lar gerekebilir. Docker Compose ile hepsini bir arada yönetelim:
cat > docker-compose.mock.yml << 'EOF'
version: '3.8'
services:
# Ana API mock'u
json-server:
image: node:18-alpine
working_dir: /app
volumes:
- ./mock-data:/app
command: >
sh -c "npm install -g json-server &&
json-server --watch db.json
--routes routes.json
--middlewares middleware.js
--host 0.0.0.0
--port 3001"
ports:
- "3001:3001"
networks:
- mock-network
restart: unless-stopped
# Ödeme sistemi mock'u (WireMock)
payment-mock:
image: wiremock/wiremock:latest
ports:
- "8080:8080"
volumes:
- ./wiremock-stubs:/home/wiremock/mappings
command: --global-response-templating --verbose
networks:
- mock-network
restart: unless-stopped
# E-posta servisi mock'u
mailhog:
image: mailhog/mailhog:latest
ports:
- "1025:1025" # SMTP
- "8025:8025" # Web UI
networks:
- mock-network
restart: unless-stopped
# SMS servisi mock'u
sms-mock:
image: wiremock/wiremock:latest
ports:
- "8081:8080"
volumes:
- ./sms-stubs:/home/wiremock/mappings
command: --global-response-templating
networks:
- mock-network
restart: unless-stopped
networks:
mock-network:
driver: bridge
EOF
# Tüm mock servislerini başlat
docker compose -f docker-compose.mock.yml up -d
# Durumları kontrol et
docker compose -f docker-compose.mock.yml ps
# Logları izle
docker compose -f docker-compose.mock.yml logs -f
CI/CD Pipeline’a Entegrasyon
Mock sunucuların en büyük değeri otomatik testlerde ortaya çıkar. GitHub Actions örneği:
# .github/workflows/integration-tests.yml
cat > integration-test.sh << 'EOF'
#!/bin/bash
set -e
echo "Mock servisleri başlatılıyor..."
docker compose -f docker-compose.mock.yml up -d
echo "Servislerin hazır olmasını bekliyorum..."
# json-server'ın hazır olmasını bekle
timeout 30 bash -c 'until curl -sf http://localhost:3001/products > /dev/null; do sleep 1; done'
# WireMock'un hazır olmasını bekle
timeout 30 bash -c 'until curl -sf http://localhost:8080/__admin/health > /dev/null; do sleep 1; done'
echo "Servisler hazır. Testler çalıştırılıyor..."
# Ürün listesi testi
PRODUCTS=$(curl -sf -H "Authorization: Bearer test-token-123" http://localhost:3001/products)
PRODUCT_COUNT=$(echo $PRODUCTS | jq length)
if [ "$PRODUCT_COUNT" -lt 1 ]; then
echo "HATA: Ürün listesi boş döndü!"
exit 1
fi
echo "Ürün listesi testi geçti. ($PRODUCT_COUNT ürün)"
# Ödeme servisi testi
PAYMENT_RESPONSE=$(curl -sf -X POST http://localhost:8080/v1/charges
-H "Authorization: Bearer sk_test_test123"
-H "Content-Type: application/json"
-d '{"amount": 5000, "currency": "try"}')
PAYMENT_STATUS=$(echo $PAYMENT_RESPONSE | jq -r '.status')
if [ "$PAYMENT_STATUS" != "succeeded" ]; then
echo "HATA: Ödeme mock'u beklenen yanıtı döndürmedi!"
exit 1
fi
echo "Ödeme servisi testi geçti."
echo "Tüm testler başarılı!"
# Temizlik
docker compose -f docker-compose.mock.yml down
EOF
chmod +x integration-test.sh
Pratik İpuçları ve Sık Karşılaşılan Sorunlar
Birkaç yıllık deneyimden çıkardığım önemli notlar:
Port çakışması sorunları: Yerel ortamda çalışırken port 3000 veya 8080 genellikle başka bir şey tarafından kullanılıyor olabilir. lsof -i :3001 komutu ile hangi process’in portu kullandığını görebilirsin.
CORS sorunları: Frontend doğrudan tarayıcıdan mock API’ye istek atarken CORS hatası alabilirsin. json-server varsayılan olarak CORS’u açık bırakır ama WireMock için özel header eklemen gerekebilir.
Mock verilerinin sıfırlanması: json-server db.json dosyasını gerçekten değiştirir. Yani POST/DELETE istekleri veriyi kalıcı olarak değiştirir. git checkout db.json ile sıfırlayabilirsin. Ya da --read-only flag’ini kullanarak sadece GET isteklerine izin verebilirsin:
json-server --watch db.json --read-only --port 3001
Mock ve gerçek ortam arasında geçiş: Environment variable kullanarak mock/gerçek arasında kolayca geçiş yapabilirsin:
# .env.development
API_BASE_URL=http://localhost:3001
PAYMENT_API_URL=http://localhost:8080
# .env.production
API_BASE_URL=https://api.sirketim.com
PAYMENT_API_URL=https://api.stripe.com
WireMock admin API’si: Çalışan WireMock’a yeni mapping eklemek için restart gerekmez:
# Yeni mapping ekle
curl -X POST http://localhost:8080/__admin/mappings
-H "Content-Type: application/json"
-d '{
"request": {"method": "GET", "url": "/api/test"},
"response": {"status": 200, "body": "OK"}
}'
# Tüm mapping'leri listele
curl -s http://localhost:8080/__admin/mappings | jq '.mappings[].name'
# Belirli bir mapping'i sil
curl -X DELETE http://localhost:8080/__admin/mappings/MAPPING_UUID
# Tüm istekleri logla
curl -s http://localhost:8080/__admin/requests | jq '.requests[].request.url'
Mock sunucu loglarını izleme: Hangi isteklerin geldiğini görmek için:
# WireMock'a gelen tüm istekleri gör
watch -n 2 'curl -s http://localhost:8080/__admin/requests | jq "[.requests[].request | {method, url}]"'
# json-server zaten terminal'e tüm istekleri yazdırır
# Docker log'ları için:
docker logs -f wiremock --tail 50
Sonuç
Mock sunucuları, modern yazılım geliştirmenin vazgeçilmez bir parçası haline geldi. Doğru araçla kurulmuş bir mock ortamı hem geliştirme hızını artırır hem de test kalitesini iyileştirir.
Hangi aracı seçmen gerektiğine dair kısa bir rehber:
- json-server: Hızlı prototip, basit CRUD API’ler, başlangıç seviyesi ekipler
- WireMock: Karmaşık istek eşleştirme, şartlı response’lar, harici servis simülasyonu
- Mockoon: GUI gerektiren ekipler, non-teknik üyelerin de kullanacağı ortamlar
- Docker Compose kombinasyonu: Birden fazla harici servisin olduğu büyük projeler
En önemli tavsiyem şu: Mock ortamını gerçek ortamla mümkün olduğunca örtüştür. Mock response’larının gerçek API’den gelen verilerle aynı yapıda olduğundan emin ol. Aksi halde mock’ta geçen testler üretimde patlayabilir. Düzenli olarak gerçek API dökümantasyonunu (OpenAPI/Swagger) kontrol edip mock’larını güncelle.
Bir sonraki adım olarak Contract Testing’e bakmanı öneririm. Pact gibi araçlar, mock’larının gerçek API ile senkronize kalmasını otomatik olarak doğrular. Ama bu ayrı bir yazının konusu.
