Kod yazmadan yük testi: kullanıcı akışı, değişkenler, kontroller ve eşikler
Yük testi araçlarının çoğu bir betik dili ister: test bir programdır, yazmak ve bakımını yapmak bir geliştiricinin işidir. Oysa bir yük testinin büyük kısmı her seferinde aynı birkaç işten oluşur: istek göndermek, cevaptan bir değer alıp sonraki isteğe koymak, cevabı doğrulamak, beklemek ve sonucu bir eşikle karşılaştırmak. Bu rehber bu işlerin her birinin bir formla nasıl yapıldığını ve nerede gerçekten koda ihtiyaç duyulduğunu anlatır.
Bir yük testi betiği ne yapar?
- İstekler: adres, metot, header'lar ve gövde. Bir kullanıcı akışı bir istek listesidir: giriş, listeleme, sepete ekleme, ödeme.
- Korelasyon: bir cevaptan değer almak (token, kimlik, CSRF değeri) ve sonraki isteklerde kullanmak. Bu yapılmazsa akış ikinci adımda kırılır.
- Test verisi: her sanal kullanıcının farklı bir hesapla, farklı ürünlerle çalışması. Hep aynı veri önbellekleri ölçer.
- Doğrulama: 200 dönen ama yanlış içerik taşıyan bir cevap da hatadır. Durum kodu, gövdedeki bir alan ya da süre kontrol edilir.
- Yük modeli ve düşünme süresi: kaç kullanıcı, hangi hızda, ne kadar süre; gerçek kullanıcıların adımlar arasında beklemesi (yük testi türleri).
- Eşikler: testin sonunda "geçti" ya da "kaldı" diyecek ölçütler; p95 gecikme ve hata oranı gibi (p95 ve p99 rehberi).
Betik yerine form
Kodsuz bir editörde bu işlerin her biri bir alandır. Önemli olan, formun bir betiğin yaptığı işi gerçekten yapabilmesidir: korelasyon olmadan bir form yalnız tek tek istekleri yükler; veri dosyası olmadan her kullanıcı aynı hesabı kullanır; eşik olmadan sonuç bir grafiktir, karar değil. Bir araca bakarken bu dördünü sorun: cevaptan değer alınabiliyor mu, test verisi dosyadan okunabiliyor mu, cevap içeriğiyle doğrulanabiliyor mu, sonuç bir eşikle karara bağlanabiliyor mu.
Spitfire ile
Spitfire'da test web editöründe adım adım kurulur. Editörün sekmeleri testin parçalarıdır: Genel ve değişkenler (açıklama, değişkenler, veri dosyaları), Senaryolar ve adımlar, Eşikler, Ortamlar, Seçenekler ve JSON. Her değişiklik birkaç yüz milisaniye içinde sunucuda doğrulanır ve yükün planı önizlenir; Dene düğmesi seçili senaryodan tek bir iterasyon gönderip her adımın isteğini ve cevabını gösterir, böylece akışı yük vermeden sınarsınız.
- Adımlar: HTTP/HTTPS, WebSocket, SSE, gRPC, Kafka, MQTT, RabbitMQ, Redis, SQL ve MongoDB, aynı senaryoda karışık. Giriş gibi bir adım VU başına bir kez çalıştır ile işaretlenir; başarısız olursa sonraki iterasyonda yeniden denenir.
- Değişken çıkarma: cevaptan JSONPath, XPath, regex (yakalama grubuyla), header ya da durum koduyla bir değer alınır ve sonraki adımlarda
{{token}}gibi kullanılır; bulunamazsa kullanılacak bir varsayılan değer verilebilir. Editör önceki adımlarda çıkarılan değişkenleri öneri olarak gösterir. - Hazır değerler:
{{__VU}}(sanal kullanıcının numarası),{{__ITER}},{{$uuid}},{{$randInt 1 100}},{{$randString 16}},{{$timestamp}}ve Türkçe sahte veriler:{{fake.tckn}},{{fake.iban}},{{fake.phone}},{{fake.fullName}},{{fake.email}},{{fake.address}}ve benzerleri. - Veri dosyaları: bir CSV dosyası bir kez yüklenir ve teste bir adla bağlanır; sütunları
{{users.email}}gibi kullanılır. Satırlar sırayla, rastgele ya da tekil (her satır koşuda en fazla bir kez) dağıtılır; birden fazla runner'da sıralı ve tekil modlar satırları runner'lar arasında böler, aynı satırı iki runner kullanmaz. - Kontroller: durum kodu, süre, gövdede metin, JSONPath ve XPath ile bir alan; HTTP, gRPC, RabbitMQ ve Kafka'da header, SQL ve MongoDB'de satır sayısı. Başarısız bir kontrol iterasyonu durdurmaz, kontrol oranına sayılır.
- Yük modeli: sabit ya da basamaklı sanal kullanıcı sayısı, sabit ya da basamaklı varış hızı, ya da sabit bir iş miktarı (toplam ya da VU başına iterasyon); adımlar arasında rastgele bir düşünme süresi; yan yana çalışan, farklı zamanlarda başlayan birden fazla senaryo.
- Eşikler:
p(95)<500,rate<0.01,avg<200gibi ifadeler; bütün isteklere, bir adıma ya da bir lokasyona. Aşılınca durdur seçeneği, eşik aşıldığında (isterseniz bir gecikmeyle) koşuyu durdurur. Eşiği aşılan koşu "kaldı" olarak biter, CI'da çıkış kodu da bunu söyler (CI/CD rehberi). - Ortamlar: aynı test test, staging ve canlı için ayrı değişkenlerle ve ayrı bağlantılarla; koşuyu başlatırken ortam, ölçek yüzdesi ve tek seferlik değerler seçilir.
Testi sıfırdan kurmak zorunda da değilsiniz: bir OpenAPI/Swagger dokümanı, bir Postman koleksiyonu ya da tarayıcıdan kaydedilmiş bir HAR dosyası adımlara çevrilir (OpenAPI ve Postman rehberi); JMeter planları ve k6 betikleri içe aktarılır (geçiş rehberi).
Bir mağaza akışı: her sanal kullanıcı bir kez giriş yapar ve token'ı alır, sonra her iterasyonda rastgele bir sayfadaki ürünleri listeler, ilk ürünün kimliğini alır ve onu sepete ekler. 50 kullanıcıya 2 dakikada çıkılır, 10 dakika tutulur. Bütün isteklerin p95'i 500 ms'nin, sepete eklemenin p95'i 800 ms'nin altında kalmalı; hata oranı ilk dakikadan sonra %1'i aşarsa koşu durur. Editörde kurulan bu testin JSON sekmesindeki hali aşağıdadır; test spitfire validate ile doğrulandı.
{
"name": "Mağaza: giriş, ürün, sepet",
"variables": { "base": "https://shop.example.com", "password": "loadtest" },
"scenarios": [
{ "name": "alisveris",
"executor": { "type": "ramping-vus", "startVUs": 0,
"stages": [ { "duration": "2m", "target": 50 }, { "duration": "10m", "target": 50 },
{ "duration": "1m", "target": 0 } ] },
"steps": [
{ "id": "login", "name": "Giriş", "once": true,
"request": { "method": "POST", "url": "{{base}}/api/login",
"body": { "type": "json",
"content": "{\"email\":\"loadtest+{{__VU}}@example.com\",\"password\":\"{{password}}\"}" } },
"extract": [ { "var": "token", "from": "jsonpath", "expr": "$.token" } ],
"checks": [ { "type": "status", "value": 200 } ] },
{ "id": "products", "name": "Ürünleri listele",
"request": { "url": "{{base}}/api/products?page={{$randInt 1 20}}",
"headers": [ { "key": "Authorization", "value": "Bearer {{token}}" } ] },
"extract": [ { "var": "productId", "from": "jsonpath", "expr": "$.items[0].id" } ],
"checks": [ { "type": "status", "value": 200 },
{ "type": "jsonPath", "path": "$.items", "op": "exists" } ],
"thinkTime": { "min": "2s", "max": "5s" } },
{ "id": "cart", "name": "Sepete ekle",
"request": { "method": "POST", "url": "{{base}}/api/cart",
"headers": [ { "key": "Authorization", "value": "Bearer {{token}}" } ],
"body": { "type": "json", "content": "{\"productId\":\"{{productId}}\",\"quantity\":1}" } },
"checks": [ { "type": "status", "value": 201 },
{ "type": "latency", "op": "lt", "value": 800 } ],
"thinkTime": { "min": "1s", "max": "3s" } }
] }
],
"thresholds": [
{ "metric": "req_duration", "expr": "p(95)<500" },
{ "metric": "req_duration", "filter": { "step": "cart" }, "expr": "p(95)<800" },
{ "metric": "req_failed", "expr": "rate<0.01", "abortOnFail": true, "delayAbortEval": "1m" }
]
}JSON sekmesi testin tamamıdır: orada düzenleyip uyguladığınızda form da güncellenir. Aynı dosya komut satırında spitfire run test.json ile çalışır ve sürüm kontrolünde tutulabilir; her kayıt testin yeni bir sürümüdür.
Kodsuz yapılamayanlar
Bir senaryo, her iterasyonda baştan sona çalışan düz bir adım listesidir. Koşullar ("sepet boşsa şu adımı atla"), döngüler ("sayfa sayfa sonuna kadar git"), özel metrikler ve çıkarılan değerler üzerinde keyfi hesaplar ifade edilemez; testte çalışan bir kod da yoktur. Bunların bir kısmı farklı kurulabilir: tek seferlik adımlar, farklı zamanlarda başlayan ayrı senaryolar, çıkarma varsayılanları ve veri dosyası modları. Akışınız gerçekten dallanıyorsa (kullanıcıların bir kısmı ödeme yapar, bir kısmı yalnız gezer), her dalı ayrı bir senaryo yapıp oranlarını VU sayılarıyla ayarlamak çoğu zaman bir koşuldan daha okunaklıdır.
Spitfire tek komutla Docker'a ya da Kubernetes'e kurulur; ücretsiz sürümde bütün test özellikleri ve protokoller açıktır.