VPS üzerinde API mock ve test süreçleri nasıl kurulur?
API mock sunucuları ve test çalıştırıcılarını kendi VPS altyapınızda nasıl yöneteceğinizi öğrenin. WireMock ve Hurl kullanarak CI süreçlerinizi optimize edin.
Tek bir depoyu paylaşan iki görev
Self-hosted API mock oluşturma ve test etme birbirinden farklı iki görevdir; bunları tek bir iş gibi ele almak bir haftalık zaman kaybına yol açar. Bir mock sunucusu, CI üzerinden erişemediğiniz bir bağımlılığın (ödeme sağlayıcısı, iş ortağı API'si, hız sınırlamasına tabi bir upstream veya başka bir ekibin henüz yayınlamadığı bir servis) yerini tutar. Bir API test çalıştırıcısı ise kendi uç noktalarınızı belirli bir sırada çağırır, yanıtları doğrular ve bir yanıttan aldığı değerleri bir sonraki isteğe taşır.
Bu ikisi birbiriyle örtüşmez. Bir mock sunucusu asla başarılı veya başarısız raporu vermez. Bir test çalıştırıcısının ise bir ödeme sağlayıcısının kart reddedildiğinde ne döndürdüğüyle ilgili bir görüşü yoktur. Hali hazırda bir sunucu kiralayan çoğu ekip, her ikisini de aynı Docker Compose dosyasıyla başlatan ve aynı pull request içerisinde gözden geçiren bir yapı kullanır.
Neden API mock ve test işlemlerini kendi sunucunuzda barındırmalısınız?
Fixtürleriniz, üretim ortamındaki verilerle aynı yapıya sahiptir. Bir API testindeki istek gövdesi, ismi değiştirilmiş gerçek bir müşteri kaydıdır; ya da kimse kontrol etmediği için ismi değiştirilmemiş gerçek bir kayıttır. Kaydedilmiş stub'lar daha da kötüdür: proxy kaydı, yukarı akışın (upstream) döndürdüğü her şeyi depolar. Bu nedenle, kayıt yoluyla oluşturulan bir stub dizini, biri her dosyayı tek tek inceleyene kadar canlı token'lar ve müşteri e-posta adresleri barındırır. Barındırılan bir hizmette bu veriler, başkasının güvenlik olayı ve sizin veri ifşanız haline gelir.
İkinci neden erişilebilirliktir. Özel bir adrese bağlı bir servis, barındırılan bir çalıştırıcıdan (runner) erişilemez durumdadır; bu nedenle test hiç çalışamaz. Her geçici çözümün bir maliyeti vardır. API'yi test etmek için internete açmak, onun özel kalma nedenini ortadan kaldırır. Bir tünel veya herkese açık bir staging kopyası, bakımı gereken başka bir sistemdir ve staging kopyası, sürümler arasında üretim ortamından uzaklaşır (drift). Aynı özel ağ üzerindeki bir çalıştırıcı, servisi doğrudan çağırır ve bunların hiçbirine ihtiyaç duymaz; bu da kendi sunucunuzda barındırdığınız bir GitHub Actions runner kullanmanın arkasındaki pratik argümandır.
Hangi self-hosted mock sunucusunu çalıştırmalısınız?
Bunların her biri, sahip olduğunuz bir sunucuda container olarak çalışır. Önemli olan soru, her birinin neyi "doğruluk kaynağı" (source of truth) olarak kabul ettiğidir; çünkü container'ı yeniden oluşturmanın size hiçbir maliyetinin mi olacağını yoksa bir öğleden sonranıza mı mal olacağını bu belirler.
- WireMock, her stub'ı
mappings/dizininde bir JSON dosyası olarak tutar; büyük yanıt gövdeleri ise__files/içindedir. İmajwiremock/wiremock'dur, container içindeki kök dizini/home/wiremock'dir ve aynı zamanda kayıt yapan bir proxy olarak da çalışır. Dosyaların diskte olması, mock'un diğer tüm kodlar gibi git içinde yaşayabileceği anlamına gelir. - Mockoon CLI, tüm mock API'yi tek bir JSON veri dosyasında tutar.
npm install -g @mockoon/cliile kurun vemockoon-cli start --data ./data-file.jsonile başlatın ya da ilgili dosyayı bind mount ederekmockoon/cliimajını çalıştırın. Masaüstü uygulaması aynı dosyayı düzenlediği için, arayüzde tasarım yapmak ve sonucu commit etmek uyumlu kalır. - MockServer,
mockserver/mockserverimajından çalışır ve 1080 numaralı portu dinler. Beklentiler (expectations), kendi REST API'si üzerinden iletilir; bu durum test kodundan kullanım için pratiktir ancak dağıtım açısından risklidir: HTTP çağrısıyla oluşturulan bir beklenti, container yeniden başlatıldığında kaybolur. Kalıcı olması gereken stub'lar için JSON başlatma dosyasını kullanın. - Prism, mock'u ayrı stub dosyaları yerine OpenAPI belgenizden oluşturur.
npm install -g @stoplight/prism-cliile kurun ve ardındanprism mock openapi.yamlkomutunu çalıştırın. Container içinde-h 0.0.0.0ekleyin; çünkü Prism varsayılan olarak localhost'a bağlanır ve aksi takdirde container dışından erişilemez olur. - Microcks, büyük seçenektir: OpenAPI belgelerini ve Postman koleksiyonlarını içe aktaran, ardından bunları mock olarak sunan ve sözleşme (contract) testlerini çalıştıran bir web arayüzüdür. Tam kurulum için MongoDB ve Keycloak, ayrıca asenkron özellikleri için Kafka gerekir. Hepsi bir arada olan
microcks-uberimajı, projenin geçici kullanım için uygun olduğunu belirttiği bellek içi (in-memory) MongoDB'yi paketler; bu nedenle arayüzde oluşturulan her şeyi geçici olarak değerlendirin ve kaynak dosyaları git içinde tutun.
Hangi self-hosted API test aracı kullanılmalı?
Buradaki görev bir dizidir: kimlik doğrula, sipariş oluştur, geri oku, durumun değiştiğini doğrula. Bu, bir yanıttan alınan değerin bir sonraki istekte kullanılmasını gerektirir. Çağrılar arasında durum (state) taşıyamayan bir araç API test aracı değil, yalnızca bir sağlık kontrolü aracıdır.
- Hurl, HTTP isteklerini içeren düz metin dosyalarını tek bir binary üzerinden çalıştırır. Bir
[Captures]bölümü yanıttan değerleri çeker, bir[Asserts]bölümü bunları kontrol eder ve--testbunu bir özet ve çıkış kodu ile bir test çalıştırıcısına dönüştürür. Ağustos 2026 itibarıyla güncel sürüm 8.0.1'dir. - Bruno CLI, bir klasör dolusu
.brudosyasını çalıştırır.npm install -g @usebruno/cliile kurun ve ardındanbru run folder --env Local --reporter-junit results.xmlile çalıştırın. Koleksiyon formatı tasarım gereği dizin içindeki metin dosyalarıdır, bu nedenle farklar (diff) inceleme sırasında okunabilir durumdadır. - Newman, Postman koleksiyonlarını Postman dışında çalıştırır:
npm install -g newman, ardındannewman run collection.json -r cli,junit --reporter-junit-export results.xml. Buradaki sorun formattır. Koleksiyon tek bir dışa aktarılmış JSON bloğudur; bu nedenle düzenleme Postman içinde yapılır ve git içindeki dosya, güncelliğini yitirebilecek bir kopyadan ibarettir. - Schemathesis farklı bir kontrol türüdür. Bir OpenAPI şemasını okur ve şemanızın imkansız olduğunu belirttiği yanıtları üretmeye çalışan test senaryoları oluşturur:
uvx schemathesis run https://your.api/openapi.json. Çökmeleri ve sözleşme ihlallerini bulur; iş kurallarınız hakkında hiçbir bilgisi yoktur, bu nedenle betik tabanlı bir test paketinin yerini almak yerine onun yanında yer alır. - Hoppscotch self-hosted, web arayüzü seçeneğidir ve bir Postgres örneği gerektirir. Kurulumdan önce bu ödünleşimi (tradeoff) anladığınızdan emin olun: koleksiyonlar deponuzda değil, bir veritabanında yaşar.
Uzak durulması gereken bir araç: Step CI hala araç listelerinde görünmekte ve YAML iş akışı formatı iyi okunmaktadır, ancak depo en son Ağustos 2024'te commit almıştır. CI süreciniz ile API'niz arasında duran bir program, bakımı yapılmayan kodlar için uygun bir yer değildir.
Mock sunucusunu güvenlik duvarının arkasına alma
Aşağıdaki kurulum, WireMock'u bir ödeme sağlayıcısı yerine kullanır. Compose dosyası formatı sizin için yeniyse, VPS üzerinde Docker Compose bölümü bu kısımda varsayılan yaşam döngüsü komutlarını açıklar.
services:
mock-payments:
image: wiremock/wiremock:3.13.2
command: ["--verbose"]
volumes:
- ./mocks/payments:/home/wiremock
ports:
- "127.0.0.1:8080:8080"
restart: unless-stoppedPort üzerindeki 127.0.0.1: ön eki kritik öneme sahiptir. Yalın bir 8080:8080, mock servisini genel IP adresiniz dahil tüm arayüzlerde erişilebilir kılar ve ufw o portu reddetse bile servis erişilebilir kalır. Bunun nedeni, Docker'ın kendi kurallarını DOCKER iptables zincirine yazması ve bu kuralların ufw'nin INPUT kurallarından önce değerlendirilmesidir. Bunun yerine loopback adresine veya özel bir arayüz adresine bağlama yapın; böylece çekirdek dışarıdan gelen bağlantıları asla kabul etmez.
Test edilen servisiniz daha sonra mock servisini işaret eder. Servis aynı compose projesi içinde çalıştığında, compose servis isimlerini kendi ağı üzerinde çözdüğü için mock servisinin temel URL'si http://mock-payments:8080 olur. Servis ana makine (host) üzerinde çalıştığında ise bu adres http://127.0.0.1:8080 olur. Bunu kod içinde değil, her zaman bir ortam değişkeni (environment variable) aracılığıyla ayarlayın; aksi takdirde test URL'si üretim ortamına taşınır.
Stub dosyaları, her biri bir JSON dosyası olacak şekilde ./mocks/payments/mappings/ dizinine yerleştirilir.
{
"request": {
"method": "POST",
"urlPath": "/v1/charges",
"bodyPatterns": [{ "matchesJsonPath": "$.amount" }]
},
"response": {
"status": 201,
"headers": { "Content-Type": "application/json" },
"jsonBody": { "id": "ch_test_001", "status": "succeeded", "amount": 4200 }
}
}Servisi başlatın ve ardından nelerin yüklendiğini kontrol edin.
docker compose up -d --wait mock-payments
curl -fsS http://127.0.0.1:8080/__admin/mappings--wait, container sağlıklı olduğunu bildirene kadar bekler. Bu işlem, WireMock imajının /__admin/health uç noktasına karşı bir HEALTHCHECK içermesi sayesinde çalışır. mappings çağrısı, sunucunun okuduğu her stub'ı listeler. Yazdığınız bir stub bu listede yoksa hiç yüklenmemiş demektir: dosyanın mount edilen kök dizin yerine mappings/ altında olduğundan ve JSON formatının geçerli olduğundan emin olun.
Bir istek geldiğinde ve hiçbir stub eşleşmediğinde, WireMock 404 yanıtını döner. Yanıt gövdesi Request was not matched ile başlar ve ardından elindeki en yakın stub ile olan farkı gösterir. Herhangi bir değişiklik yapmadan önce bu farkı okuyun; çünkü bu, farklı olan alanı tam olarak belirtir. Bu genellikle stub içinde /v1/charges yazan bir yolun /v1/charge olarak görünmesi durumudur.
Testi, çağrılar arasında durumun taşındığı bir dizi olarak yazın
Hurl dosyaları düz metin dosyalarıdır. Projenin releases sayfasından .deb paketini yükleyin.
VERSION=8.0.1
curl --location --remote-name https://github.com/Orange-OpenSource/hurl/releases/download/$VERSION/hurl_${VERSION}_amd64.deb
sudo apt update && sudo apt install ./hurl_${VERSION}_amd64.debKendi API'nizi mock üzerinde çalıştıran bir test paketi tests/checkout.hurl dizininde bulunur.
POST {{base_url}}/orders
Content-Type: application/json
{
"sku": "ssd-1tb",
"amount": 4200
}
HTTP 201
[Captures]
order_id: jsonpath "$['id']"
GET {{base_url}}/orders/{{order_id}}
HTTP 200
[Asserts]
jsonpath "$.status" == "paid"
jsonpath "$.charge_id" == "ch_test_001"[Captures] bloğu, bunu iki alakasız istek yerine bir API testi yapan kısımdır. order_id, ilk yanıttan okunur ve ikinci isteğin URL'sine yerleştirilir. charge_id üzerindeki doğrulama, tüm bu sürecin temel amacıdır: servisin ödeme sağlayıcısını çağırdığını ve dönen yanıtı kaydettiğini kanıtlar; karşılaştırılan değer ise WireMock stub dosyasına yazdığınız değerdir. Artık tek bir dosya, akışın her iki yarısını da kapsamaktadır.
hurl --test --variable base_url=http://127.0.0.1:3000 \
--report-junit reports/junit.xml \
--report-json reports/json \
tests/Başarılı bir çalıştırma, her dosya için bir satır ve bir özet çıktısı verir.
tests/checkout.hurl: Success (2 request(s) in 61 ms)
Executed files: 1
Executed requests: 2 (30.1/s)
Succeeded files: 1 (100.0%)
Failed files: 0 (0.0%)
Duration: 64 msBaşarısızlık durumunda error: Assert failure ile dosya ve satır numarası yazdırılır, ardından elde edilen değer ile beklenen değer karşılaştırılır ve hurl sıfır olmayan bir çıkış kodu döndürerek CI sürecini durdurur. Eğer status, paid beklediğiniz yerde pending değerini okuyorsa, servisiniz mock yanıtını işlememiştir. Bir sonraki adım, çağrının mock'a ulaşıp ulaşmadığını gösteren /__admin/requests adresindeki WireMock istek günlüğünü incelemektir.
Test paketini kendi CI runner'ınız üzerinden tetikleyin
Aynı sunucu üzerinde kayıtlı bir runner ile iş akışı oldukça kısadır. Runner, ana makine üzerinde çalışan basit bir süreçtir; bu nedenle docker ve hurl araçlarının o makinede yüklü olması gerekir. Barındırılan bir imajdan hiçbir ayar devralınmaz.
name: api-tests
on: [push]
jobs:
hurl:
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
- name: Start the mock
run: docker compose up -d --wait mock-payments
- name: Run the suite
run: hurl --test --variable base_url=http://127.0.0.1:3000 --report-junit reports/junit.xml tests/
- name: Archive the reports
if: always()
run: install -d /srv/api-tests/reports/$GITHUB_SHA && cp -r reports/. /srv/api-tests/reports/$GITHUB_SHA/
- name: Stop the mock
if: always()
run: docker compose downArşivleme adımındaki if: always() parametresi kritiktir. Bu parametre kullanılmadığında, başarısız olan bir test çalışması kopyalama işlemini atlar; bu durumda incelemek istediğiniz raporu kaybedersiniz. Ayrıca kopyalama hedefi çalışma alanının (workspace) dışında olmalıdır, çünkü runner bir sonraki işten önce çalışma alanını temizler ve raporlar da bu temizlikle birlikte silinir.
Sadece son çalıştırmayı değil, tüm sonuçları saklayın
Her commit için oluşturulan bir JUnit XML dosyası tek bir soruya yanıt verir: test başarılı oldu mu? Bu dosyalar açılmadığı sürece okunmadıkları için, bir uç noktanın ne zaman yavaşlamaya başladığına dair bir bilgi sağlamazlar. Eğilimleri takip etmek için, her çalıştırmada aynı sunucu üzerindeki küçük bir veritabanına bir satır ekleyin. Commit SHA değerini, dosya adını, başarılı test sayısını, başarısız test sayısını ve süreyi tutan tek bir tablo yeterlidir. VPS üzerinde üretim ortamında SQLite kullanımı bu iş için makul bir yöntemdir: tek bir dosya, sunucu süreci gerektirmez ve tüm geçmiş, halihazırda aldığınız yedeklerin içinde yer alır. Hurl'ün --report-json çıktısını, iki format arasından makine tarafından okunabilir olanı olduğu için JUnit XML yerine ayrıştırın.
Container yeniden oluşturma sürecinde nelerin korunması gerekir
Mock tanımları ve test paketleri kaynak kodun bir parçasıdır. Bu dosyalar, tanımladıkları servisin yanında bir depoda tutulmalı ve uç noktayı değiştiren pull request ile birlikte güncellenmelidir. Bir web arayüzünde düzenlenen stub veya çalışma zamanında REST API üzerinden MockServer'a gönderilen bir beklenti, yalnızca o container'ın belleğinde veya ilgili aracın veritabanında var olur. docker compose down komutunu çalıştırdığınızda bu veriler silinir ve bir test yanlış nedenle başarılı olana kadar kimse durumu fark etmez. Eğer depolarınızı kendi donanımınız üzerinde çalıştırıyorsanız, kendi kendine barındırılan bir git sunucusu fixture dosyalarını ve servisi aynı güven sınırı içerisinde tutmanızı sağlar.
Şimdi pratik kurallara geçelim. Image etiketlerini sabitleyin; çünkü latest, deponuzda hiçbir değişiklik yapmasanız bile mock'un istekleri eşleştirme biçimini değiştirebilir ve bu hatanın nedenini bulmak oldukça zordur. Araç yazma iznine ihtiyaç duymadığında stub dizinlerini salt okunur (read-only) olarak mount edin. Bir mock'un stub dosyalarını asla isimlendirilmiş bir Docker volume içerisine koymayın; çünkü bu durumda volume gerçek veri kaynağı haline gelir ve git üzerindeki kopya sessizce geçersiz kalır.
Bir kural daha var ve bu genellikle gözden kaçar. Eğer stub dosyalarını bir proxy üzerinden gerçek trafiği kaydederek oluşturuyorsanız, commit etmeden önce oluşturulan her dosyayı inceleyin. Bir kayıt, upstream tarafından gönderilen verinin birebir kopyasını içerir; buna bearer token'lar ve müşteri e-posta adresleri de dahildir. Dosyayı commit etmek, bu verileri kalıcı olarak deponuza ekler; çünkü git, silinen içerikleri geçmişte tutmaya devam eder.
FAQ
API mock sunucusu ile API test çalıştırıcısı arasındaki fark nedir?
Bir mock sunucusu isteklere yanıt verir. CI ortamından çağıramadığınız bir bağımlılığın yerini tutar ve hiçbir zaman başarılı veya başarısız raporu vermez. Bir API test çalıştırıcısı ise kendi servisinize istekler gönderir, yanıtları doğrular, değerleri bir çağrıdan diğerine taşır ve bir doğrulama başarısız olduğunda sıfırdan farklı bir çıkış kodu döndürür. Bu araçlar farklı sorunları çözer; tipik bir kurulumda her ikisi de aynı anda çalışır: test çalıştırıcısı servisinizi çağırırken, servisiniz de mock sunucusunu çağırır.
Barındırılan bir CI çalıştırıcısından dahili bir API'yi test edebilir miyim?
Dış dünyaya açmadan test edemezsiniz. Barındırılan bir çalıştırıcı ağınızın dışında yer alır, bu nedenle özel bir adrese bağlı bir servise ulaşamaz. Seçenekleriniz API'yi yayınlamak, bir tünel çalıştırmak veya herkese açık bir staging kopyası tutmaktır; ancak bunların her biri hata yapabilecek veya sızıntıya yol açabilecek yeni bir sistem ekler. Aynı özel ağ üzerindeki bir çalıştırıcı servisi doğrudan çağırır; ekiplerin bu işi kendi sunucularında barındırmasının temel pratik nedeni budur.
Mock taslakları ve API test paketleri nerede tutulmalıdır?
Git üzerinde, tanımladıkları servisin yanında tutulmalıdır. Tanımları dosya olarak saklayan araçlar (örneğin WireMock'un mappings/ dizini, Mockoon'un veri dosyası, Hurl dosyaları ve Bruno'nun .bru klasörü), kod incelemesi yapmanıza ve hiçbir maliyeti olmayan container yeniden oluşturma süreçlerine olanak tanır. Tanımları bir veritabanında veya web arayüzünde saklayan araçlar ise bir yedekleme planına ve dışa aktarma adımına ihtiyaç duyar; dışa aktarma işlemi, container silinene kadar genellikle unutulan kısımdır.
Taslak doğru görünmesine rağmen mock sunucum neden 404 döndürüyor?
WireMock bir taslağı yalnızca tam eşleşme durumunda sunar. Eşleşmeyen bir istek, gövdesi Request was not matched ile başlayan ve en yakın taslakla farkları gösteren bir 404 yanıtı alır; bu farklar hangi alanın uyuşmadığını belirtir. Yaygın nedenler arasında yolun sonundaki eğik çizgi, taslağın gerektirdiği ancak istemcinizin göndermediği bir Content-Type başlığı, değişken bir segment için taslağın urlPathPattern gerektirdiği yerde urlPath kullanılması ve yük ile eşleşmeyen bir gövde eşleştiricisi bulunur. İsteğin mock sunucusuna ulaştığını doğrulamak için önce /__admin/requests kısmını kontrol edin.
Bir staging ortamım varsa yine de mock kullanmalı mıyım?
Evet, iki nedenden dolayı. Kontrolünüz altında olmayan bir yukarı akışın (upstream) staging kopyası da zaman zaman çöker veya hız sınırlaması uygular; bu durumda test paketiniz kodunuzla ilgisi olmayan nedenlerle başarısız olur. Ayrıca staging ortamı, reddedilen bir kart veya ağ geçidi zaman aşımı gibi test etmeniz gereken yanıtları her zaman üretemez. Bir mock sunucusu bunları yerel ağ hızında ve talep üzerine döndürür; bu da sandbox ortamında dakikalar süren bir test paketini saniyeler süren bir sürece dönüştürür. Staging ortamını sürüm öncesi son kontrol için saklayın ve CI süreçlerinde mock kullanın.