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ünler
  • GET /products/1 – ID’si 1 olan ürün
  • POST /products – yeni ürün ekle
  • PUT /products/1 – ürünü güncelle
  • DELETE /products/1 – ürünü sil
  • GET /products?category=electronics – filtreleme
  • GET /products?_sort=price&_order=desc – sıralama
  • GET /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.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir