परफ़ॉर्मेंस टेस्ट स्क्रिप्ट लिखना

Performance test script वह program है जिसे हर virtual user (VU) चलाता है: एक user journey की requests उनके क्रम में, साथ में वह data, pauses और checks जो उसकी सैकड़ों copies को सैकड़ों अलग-अलग लोगों जैसा बर्ताव करवाते हैं। यह पिछले lesson के workload model को ऐसी चीज़ में बदलती है जिसे load tool चला सके, और ज़्यादातर काम हर run में हर request को valid बनाना है।

उदाहरण: एक checkout script login करती है, cart में product जोड़ती है और order place करती है। अगर वह recording के समय capture हुई session cookie और CSRF value ही दोबारा भेजती है, तो server login के बाद की हर request reject कर देता है; अगर सभी 200 virtual users एक ही account share करते हैं, तो वे एक ही cart share करते हैं। दोनों runs charts बनाते हैं; कोई भी checkout को नहीं मापता।

यह क्यों ज़रूरी है

टूटी हुई script शायद ही कभी खुलकर fail होती है। Reject हुई requests milliseconds में लौट आती हैं, error pages 200 OK के साथ आ सकते हैं, और जिस run में ज़्यादातर requests refuse हुईं वह असली system से बेहतर response times दिखा सकता है। फिर release और capacity के फ़ैसले ऐसे load पर होते हैं जो कभी पड़ा ही नहीं।

Tester के लिए scripting वह जगह है जहाँ workload model असली system से मिलता है: server हर session के लिए कौन-सी values issue करता है, हर journey कौन-सा test data खपाती है, और सही response कैसे पहचाना जाए। यही जवाब तय करते हैं कि results पर भरोसा किया जा सकता है या नहीं।

Recording या लिखना, transactions और timers

Script record भी की जा सकती है और लिखी भी। Recording journey पर click करते समय traffic capture करती है: JMeter का HTTP(S) Test Script Recorder browser और server के बीच proxy की तरह काम करता है, और Grafana browser recording (HAR file) को k6 script में बदलने वाला converter देता है। Recording तेज़ है, लेकिन सब कुछ capture करती है: analytics calls, ऐसे third-party resources जिनका load test करने की आपको permission नहीं, और उस दिन server की issue की हुई हर value। API contract या browser के network panel से लिखी script में कम और साफ़ requests होती हैं, और APIs के लिए यही आम है; कई testers पहले record करके देखते हैं कि क्या भेजा जाता है, फिर script दोबारा लिखते हैं।

दोनों हालात में script को transactions से बनाएँ: requests के नामित groups जो user का एक step बनाते हैं, जैसे "log in", जिन्हें साथ में time करके response time targets से मिलाया जाता है। JMeter इसके लिए Transaction Controller इस्तेमाल करता है, k6 group()। Timers transactions के बीच think time जोड़ते हैं, ताकि pauses response time में न गिने जाएँ। नीचे का JMeter plan इस lesson में इस्तेमाल हर हिस्सा दिखाता है।

Test Plan: checkout
└─ Thread Group: 200 threads, ramp-up 300 s, duration 2,100 s (ramp-up included)
   ├─ CSV Data Set Config: users.csv -> email, password, sku
   ├─ HTTP Cookie Manager: one cookie store per thread (session cookies)
   ├─ Transaction Controller: 01 log in
   │  └─ HTTP Request: POST /api/login
   │     ├─ JSON Extractor: $.csrfToken -> csrf (default value NOT_FOUND)
   │     └─ Response Assertion: response code equals 200
   ├─ Flow Control Action: pause (think time, outside the transactions)
   │  └─ Uniform Random Timer: 3,000 ms + random 0 to 4,000 ms
   └─ Transaction Controller: 02 add to cart
      └─ HTTP Request: POST /api/cart/items
         ├─ HTTP Header Manager: X-CSRF-Token = ${csrf}
         └─ JSON Assertion: $.sku equals ${sku}

आज़माएँ: Concurrent रिक्वेस्ट टेस्ट जनरेटर

Correlation और parameterisation

Correlation का मतलब है किसी response से dynamic value पकड़कर बाद की request में भेजना: session ids; CSRF values, यानी anti-forgery tokens जिन्हें server form के hidden field या JSON body में रखता है और हर बदलाव के साथ वापस चाहता है; script की बनाई चीज़ों के ids, जैसे order। Recorded script में recording वाले दिन की values होती हैं, जो अब expire हो चुकी हैं या किसी दूसरे session की हैं। Session cookies आसान case हैं: JMeter का HTTP Cookie Manager और k6 का cookie jar उन्हें हर virtual user के लिए browser की तरह रखते और दोबारा भेजते हैं। Bodies, headers और URLs की बाक़ी values आपको ख़ुद correlate करनी होती हैं, चार steps में।

Parameterisation fixed values को data file के variables से बदलती है, ताकि हर virtual user अलग इंसान हो, अपने account, product और request payload के साथ, जैसे search term या delivery address। एक shared account मतलब सबके लिए एक session, एक cart और database की वही rows; एक product id मतलब हर product page cache से आ सकता है। जो data journey में खप जाता है, जैसे sign-up के email addresses या एक बार चलने वाले coupons, उसके लिए हर iteration की एक row चाहिए, हर user की नहीं। JMeter file को CSV Data Set Config से पढ़ता है, k6 open() और SharedArray से।

  • Find: journey को दो बार, या दो accounts से, record करके तुलना करें; जो values अलग हैं और किसी पहले response से आईं, वही candidates हैं।
  • Extract: value issue करने वाले response के ठीक बाद, JSON path, regular expression, CSS selector या उसके आसपास के text (boundaries) से; k6 res.json() से पढ़ता है या body में खोजता है।
  • Replace: जहाँ-जहाँ recorded value थी, bodies, headers और URLs में, वहाँ variable इस्तेमाल करें।
  • Verify: check करें कि extraction को कुछ मिला; NOT_FOUND जैसी default value खाली value भेजने की बजाय failure दिखा देती है।
Replay की गई recording, recording वाले दिन की CSRF value भेजती है, इसलिए login के बाद हर request को 403 मिलता है; correlated script हर virtual user को users.csv की अपनी row और उसके अपने login की fresh value देती है।
  • Recording जैसी की तैसी replay

    • वर्चुअल यूज़र k6 script
    • सर्वर staging.example
    1. 1 वर्चुअल यूज़र से सर्वर तक: Log in POST /api/login
    2. 2 सर्वर से वर्चुअल यूज़र तक: नया session और नई CSRF value जवाब {"csrfToken":"f3a9…"}
    3. 3 वर्चुअल यूज़र से सर्वर तक: Recording वाले दिन की value भेजता है X-CSRF-Token: 5d0b…
    4. 4 सर्वर से वर्चुअल यूज़र तक: Reject हुई जवाब 403 Forbidden फ़ेल

    परिणाम: Login के बाद हर request fail होती है, और तेज़ी से। फ़ेल

  • Correlated और parameterised

    • डेटा फ़ाइल users.csv
    • वर्चुअल यूज़र k6 script
    • सर्वर staging.example
    1. 1 डेटा फ़ाइल से वर्चुअल यूज़र तक: अपनी row: account और product lt.user0007@…, SKU-10482
    2. 2 वर्चुअल यूज़र से सर्वर तक: उसी account से login POST /api/login
    3. 3 सर्वर से वर्चुअल यूज़र तक: Session cookie और fresh CSRF value जवाब Set-Cookie: sid=Q7mT… {"csrfToken":"f3a9…"}
    4. 4 वर्चुअल यूज़र आंतरिक रूप से: Value extract और check करना न मिले तो iteration ख़त्म csrf = res.json('csrfToken')
    5. 5 वर्चुअल यूज़र आंतरिक रूप से: Think time, random 3 से 7 s
    6. 6 वर्चुअल यूज़र से सर्वर तक: Row का product जोड़ना, value के साथ cookie jar session cookie जोड़ता है X-CSRF-Token: f3a9…
    7. 7 सर्वर से वर्चुअल यूज़र तक: इस user की cart line जवाब 201 {"sku":"SKU-10482",…}
    8. 8 वर्चुअल यूज़र आंतरिक रूप से: Check: status 201 और वही SKU पास

    परिणाम: हर virtual user सिर्फ़ वही values भेजता है जो इसी iteration में उसे issue हुईं। पास

पाठ संस्करण
Recorded value replayed (not correlated)
POST /api/cart/items
X-CSRF-Token: 5d0b8a42                                  <- from the recording day
-> 403 Forbidden, for every virtual user

Correlated and parameterised
POST /api/login
{"email":"lt.user0007@example.com","password":"..."}    <- row 7 of users.csv
-> 200 OK
   Set-Cookie: sid=Q7mT2xVb; Path=/; Secure; HttpOnly   <- kept by the cookie jar
   {"csrfToken":"f3a9c1e7"}                             <- extract: csrf

POST /api/cart/items
Cookie: sid=Q7mT2xVb                                    <- sent by the cookie jar
X-CSRF-Token: f3a9c1e7                                  <- send: csrf
{"sku":"SKU-10482","qty":1}                             <- row 7 of users.csv
-> 201 Created  {"sku":"SKU-10482","qty":1}             <- check: same SKU

आज़माएँ: लोड टेस्ट डेटा जनरेटर आज़माएँ: JMeter CSV जनरेटर आज़माएँ: CSV डेटासेट जनरेटर

Think time, pacing और response checks

Think time और pacing आपको workload model से मिले; script में ये बस कुछ lines हैं। Steps के बीच random pause लें: k6 में sleep(3 + Math.random() * 4), JMeter में Uniform Random Timer। Pacing के लिए rate tool पर छोड़ें (k6 के arrival-rate executors, JMeter का Constant Throughput Timer), या हर iteration के interval का बाक़ी समय sleep करें। Pauses के बिना कुछ virtual users ही बहुत-से लोगों जितना traffic भेजते हैं, और test वह load नहीं डालता जो model में तय है।

Load tool हर response का time मापता है, उसमें चाहे जो हो, इसलिए checks के बिना तेज़ error तेज़ success गिना जाता है। 4xx और 5xx status default रूप से पकड़े जाते हैं: JMeter sample (एक request का उसका record) को failed mark करता है, k6 उसे http_req_failed में गिनता है। लेकिन कई failures success status के साथ आते हैं: session खोने के बाद login page, 200 OK वाला error page, "status":"error" कहने वाली body, गलत data से खाली search result। हर response का status और कुछ ऐसा check करें जो सिर्फ़ सही response में हो।

  • ऐसा content check करें जो step की सफलता साबित करे, जैसे किसी field की value या product का नाम, हर page पर दिखने वाला text नहीं।
  • Failed checks को असरदार बनाएँ: k6 में failed check() run को सिर्फ़ checks: ['rate>0.99'] जैसे threshold के ज़रिए fail करता है; JMeter में failed assertion sample को failed mark करता है।
  • टूटी journey ख़त्म करें: login fail हो तो बाक़ी steps खाली value के साथ भेजने की बजाय iteration रोक दें, लेकिन pause रहने दें, वरना fail हुआ user पूरी रफ़्तार से login दोहराता रहेगा।

आज़माएँ: परफ़ॉर्मेंस टेस्ट डेटा जनरेटर

Load profile, एक user से validation और generator की capacity

Load profile configuration है, journey का code नहीं। आम तौर पर इसके तीन phases होते हैं: ramp-up virtual users को धीरे-धीरे जोड़ता है, steady state मापने के दौरान target load को स्थिर रखता है, और ramp-down उन्हें हटाता है। k6 में हर phase stages की एक entry है, या आप कोई scenario executor चुनते हैं; JMeter thread group में आप threads (हर virtual user का एक thread), ramp-up period और duration set करते हैं, और duration में ramp-up शामिल है (standard thread group में ramp-down नहीं होता)। Profile चुनना अगले lesson, load testing, का विषय है। Profile, base URL और data file को journey code से बाहर रखें, ताकि वही script एक user से भी चले और कई users से भी।

कोई भी load डालने से पहले एक virtual user को एक iteration के लिए full logging के साथ चलाएँ: JMeter में View Results Tree, k6 में --http-debug=full। हर request और response पढ़ें, पक्का करें कि हर extracted value fresh है और सही user और product के लिए order मौजूद है। कुछ iterations दोहराएँ और हर transaction का time लिखें: यही single-user baseline है जिससे load वाले results की तुलना होती है, और जो step एक user के लिए भी धीमा है वह अभी से finding है। फिर कुछ users को कुछ मिनट चलाएँ, ताकि shared accounts, टकराता data और ख़त्म होती rows पकड़ी जाएँ।

  • Load generator की सीमाएँ हैं: उसका CPU, memory या network saturate हो तो requests उसी के अंदर queue होती हैं और run system की बजाय generator को मापता है। उसके resources देखें, JMeter को non-GUI mode में View Results Tree जैसे debugging listeners के बिना चलाएँ, और k6 में discardResponseBodies set करें और responseType: 'text' सिर्फ़ उन requests पर माँगें जिनकी body कोई check पढ़ता है।
  • एक machine काफ़ी न हो तो load बाँटें और हर generator को data file का अपना हिस्सा दें, वरना दो generators एक ही users से login करेंगे।

आज़माएँ: रिस्पॉन्स टाइम एनालाइज़र

Worked example: k6 में checkout script की शुरुआत

एक online store के test environment में users.csv में 500 test accounts हैं, हर row में email address, password और एक product SKU। नीचे की script checkout के पहले दो steps cover करती है; बाक़ी steps इसी pattern पर चलते हैं। Session cookie बिना किसी code के k6 के cookie jar में चलती है।

  • Data: SharedArray file को सभी virtual users के लिए एक बार load करता है; __VU (1, 2, 3 …) हर एक को अपनी row देता है: 200 users, 200 accounts। यह सीधा split मानकर चलता है कि values में commas या quotes नहीं हैं।
  • Correlation: login response का csrfToken X-CSRF-Token में जाता है; value न मिले तो check fail होता है और iteration एक pause के बाद ख़त्म होता है।
  • Thresholds: अगर 1% या उससे ज़्यादा checks fail हों या cart step का p95 800 ms तक पहुँचे तो run fail होता है।
  • Profile: 200 users तक 5 मिनट ramp-up, 30 मिनट steady state, 3 मिनट ramp-down। पहले k6 run --vus 1 --iterations 1 --http-debug=full -e BASE_URL=https://staging.example checkout.js से validate करें।
import http from 'k6/http';
import { check, group, sleep } from 'k6';
import { SharedArray } from 'k6/data';

// users.csv: a header row, then one test account per row: email,password,sku
const users = new SharedArray('users', () =>
  open('./users.csv').split(/\r?\n/).slice(1).filter(Boolean).map((l) => l.split(',')));

export const options = {
  stages: [
    { duration: '5m', target: 200 },  // ramp-up
    { duration: '30m', target: 200 }, // steady state
    { duration: '3m', target: 0 },    // ramp-down
  ],
  thresholds: {
    checks: ['rate>0.99'],
    'http_req_duration{name:add-to-cart}': ['p(95)<800'],
  },
};

const BASE = __ENV.BASE_URL;
const think = () => sleep(3 + Math.random() * 4); // think time: random 3 to 7 s

function field(res, path) { // one JSON field, or null when the body is not JSON
  try { const v = res.json(path); return v === undefined ? null : v; }
  catch (e) { return null; }
}

export default function () {
  const [email, password, sku] = users[(__VU - 1) % users.length]; // own row per VU

  const csrf = group('01 log in', () => {
    const res = http.post(`${BASE}/api/login`, JSON.stringify({ email, password }), {
      headers: { 'Content-Type': 'application/json' }, tags: { name: 'login' },
    });
    const token = field(res, 'csrfToken'); // correlation: extract the dynamic value
    check(res, {
      'login: status 200': (r) => r.status === 200,
      'login: CSRF value received': () => !!token,
    });
    return token;
  });
  if (!csrf) { think(); return; } // end the iteration after a pause: the check failed
  think();

  group('02 add to cart', () => {
    const res = http.post(`${BASE}/api/cart/items`, JSON.stringify({ sku, qty: 1 }), {
      headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrf },
      tags: { name: 'add-to-cart' },
    });
    check(res, {
      'cart: status 201': (r) => r.status === 201,
      'cart: line has this SKU': (r) => field(r, 'sku') === sku,
    });
  });
  think();
}

आम गलतियाँ

  • Recording को जैसी है वैसी replay करना: पुरानी session और CSRF values से login के बाद की हर request reject होती है, और recorded third-party calls ऐसी services पर load डालती हैं जिन्हें टेस्ट करने की आपको permission नहीं।
  • सभी virtual users को एक account या एक product से चलाना, जिससे sessions टकराते हैं और cache हर चीज़ का जवाब देता है।
  • सिर्फ़ status codes पर भरोसा करना, जिससे login redirects और 200 OK वाले error pages तेज़ successes जैसे दिखते हैं।
  • Think time छोड़ देना, या transaction के अंदर pause लेना, जहाँ pause उसके time में गिना जा सकता है: k6 का group() उसे हमेशा गिनता है।
  • एक user वाले run के बिना सीधे full load से शुरू करना, जिससे script errors हज़ारों failures में दब जाते हैं।
  • Load generator को नज़रअंदाज़ करना: saturated machine, या debugging listeners के साथ GUI mode में चलता JMeter, system को असल से धीमा दिखाता है।

QA का नज़रिया

Script को testware मानें जिसे review चाहिए। Developers से पूछें कि कौन-सी values हर session या हर request पर issue होती हैं और journey असल में कौन-सी requests भेजती है; पक्का करें कि test environment में काफ़ी test accounts, products और payment stubs हैं; और product owner के साथ तय करें कि हर step का सही response कैसा दिखता है, ताकि checks सिर्फ़ status codes नहीं, business results टेस्ट करें।

Script को उसके data generator और एक छोटे note के साथ version control में रखें: application version, correlate की गई values, data files और एक user वाले run का command। Single-user baseline को build number के साथ record करें और हर release के बाद एक user वाला run दोहराएँ: rename हुआ field या नई security value correlation को चुपचाप तोड़ देती है।

अभ्यास

Recorded booking script में dynamic values खोजें

आपका काम

आपने एक test account से clinic appointment booking की journey record की है; छोटी की गई recording नीचे है। तय करें कि कौन-सी values correlate करनी हैं और कौन-सी parameterise, हर response के लिए एक check लिखें, और बताएँ कि 100 users वाले load test से पहले script को कैसे validate करेंगे।

सामग्री
1  GET /login
   200  Set-Cookie: sid=A81kQ2; Path=/; Secure; HttpOnly
        <input type="hidden" name="_csrf" value="9c2e71f0">
2  POST /login   _csrf=9c2e71f0&email=qa.patient17@example.com&password=...
   303  Location: /home   Set-Cookie: sid=Zr40Pm; Path=/; Secure; HttpOnly
3  GET /api/slots?clinic=12&date=2026-11-03
   200  [{"slotId":"s-55871","time":"09:30"},{"slotId":"s-55872","time":"09:45"}]
4  POST /api/bookings   X-CSRF-Token: 9c2e71f0
   {"slotId":"s-55871","patientId":"p-1043"}
   201  Location: /api/bookings/b-77310
5  GET /api/bookings/b-77310
   200  {"bookingId":"b-77310","status":"confirmed","time":"09:30"}
चरण
  1. Server की issue की हुई हर value, वह किस response से आई और उसे कैसे extract करेंगे, mark करें।
  2. वे values mark करें जो virtual users के बीच अलग होनी चाहिए, और उन्हें देने वाली data file।
  3. हर response के लिए एक ऐसा check लिखें जिसे सिर्फ़ सही response pass करे।
  4. एक user और कुछ users वाले runs की योजना बनाएँ।
मॉडल उत्तर दिखाएँ
मॉडल उत्तर

Correlate: responses 1 और 2 से sid cookie (cookie manager या jar संभालता है; पक्का करें कि login पर id बदलता है); response 1 के hidden field से _csrf (CSS selector या regular expression), जो request 2 में और request 4 में X-CSRF-Token के रूप में जाता है, या login नई value issue करे तो सबसे नई; response 3 से slotId (JSON path, हमेशा पहला नहीं बल्कि कोई free slot); response 4 के Location header से booking id, request 5 के लिए।

Parameterise: email और password, हर virtual user के लिए एक test account; patientId, जो किसी response में नहीं दिखता, account की data row में; clinic और date, recorded fixed 2026-11-03 की बजाय हमेशा भविष्य की कोई date, इस तरह फैलाए हुए कि 100 users उन्हीं दो slots के लिए न टकराएँ, जब तक वही contention टेस्ट न करना हो।

Checks: (1) form में _csrf value; (2) /home पर 303 redirect, वापस /login नहीं (tool redirects follow करता हो तो final URL check करें); (3) कम से कम एक slot वाला JSON array; (4) Location header के साथ 201; (5) "status":"confirmed" और booked time।

Validation: एक user, एक iteration, full logging; पक्का करें कि values fresh हैं और उस patient की booking मौजूद है, फिर baseline के लिए कुछ iterations। उसके बाद 5 users कुछ मिनट तक, ताकि shared accounts, slot collisions और ख़त्म होता data पकड़ा जाए, और runs के बीच slots reset हों। तभी 100 users, load generator पर नज़र रखते हुए।

अक्सर पूछे जाने वाले सवाल

पहली script के लिए JMeter इस्तेमाल करूँ या k6?

दोनों वही skills सिखाते हैं। JMeter test plan को GUI में recording, extraction और data files के तैयार elements से बनाता है, और असली test के लिए command line से चलता है। k6 scripts JavaScript files हैं जो version control में रहती हैं और CI pipeline में फ़िट होती हैं। जो tool आपकी team पहले से चलाती है वही लें; correlation, parameterisation, checks और validation दोनों में एक ही तरह काम करते हैं।

क्या Selenium या Playwright जैसी browser automation script से load test कर सकते हैं?

बड़े पैमाने पर नहीं। हर browser protocol-level virtual user से कहीं ज़्यादा CPU और memory लेता है, इसलिए एक machine पर कहीं कम browsers चल पाते हैं। Protocol-level scripts HTTP requests सीधे भेजती हैं। Browser-level tests protocol-level load के साथ उपयोगी हैं, यह मापने के लिए कि system पर load रहते हुए एक असली user page में क्या अनुभव करता है, जैसे rendering time।

मुझे test data की कितनी rows चाहिए?

User को पहचानने वाले data, जैसे accounts, के लिए हर virtual user की कम से कम एक row। Journey में खपने वाले data, जैसे नए registrations या एक बार चलने वाले coupons, के लिए virtual users की संख्या को उन iterations से गुणा करें जो हर user run में पूरी करता है, और कुछ margin जोड़ें। Product ids जैसा पढ़ा जाने वाला विविध data इतना बड़ा होना चाहिए कि cache production जैसा बर्ताव करे, जैसा workload model बताता है।

संदर्भ

  • Certified Tester Performance Testing (CT-PT)ISTQB · 2018performance testing पर ISTQB का specialist पाठ्यक्रम: बुनियादी अवधारणाएँ, performance measurements, टेस्ट गतिविधियाँ व कार्य, और टूल।
  • Grafana k6 documentationGrafana LabsGrafana k6 का आधिकारिक दस्तावेज़ — एक open-source और extensible load व performance testing टूल।
  • Apache JMeter User's ManualApache Software FoundationJMeter से लोड टेस्ट कैसे बनाएँ, पैरामीटराइज़ करें और चलाएँ, और उसके लिसनर व रिपोर्ट कैसे पढ़ें।
  • ISTQB GlossaryISTQBटेस्टिंग शब्दों की मानक परिभाषाएँ — टेस्ट केस, रिग्रेशन टेस्टिंग, सेवेरिटी, प्रायोरिटी, बाउंड्री वैल्यू एनालिसिस आदि।

Mutqan tools से अभ्यास करें

JMeter CSV Data Set Config के लिए सही quoting, delimiter और encoding वाली CSV फ़ाइलें बनाएँ।

ऑटोमेशन

कस्टम कॉलम, डेटा टाइप, पंक्ति संख्या और डिलिमिटर के साथ CSV फ़ाइलें जनरेट करें।

टेस्ट डेटा

और गहराई से

इस topic में आपकी प्रगति

QA रोडमैप का हिस्सा