كتابة سكربت اختبار الأداء
سكربت اختبار الأداء (Performance Test Script) هو البرنامج الذي يشغّله كل مستخدم افتراضي (Virtual User, VU): طلبات رحلة مستخدم واحدة بترتيبها، مع البيانات وفترات التوقف والفحوص التي تجعل مئات النسخ منه تتصرف كمئات الأشخاص المختلفين. وهو يحوّل نموذج عبء العمل من الدرس السابق إلى شيء تستطيع أداة الحمل تشغيله، ومعظم العمل فيه هو جعل كل طلب صالحاً في كل تشغيل.
مثال ذلك: سكربت لإتمام الشراء (Checkout) يسجّل الدخول، ويضيف منتجاً إلى السلة، ثم يُنشئ طلب الشراء. فإذا أعاد إرسال ملف تعريف الارتباط (Cookie) الخاص بالجلسة وقيمة CSRF الملتقطين وقت تسجيل الرحلة، رفض الخادم كل طلب بعد تسجيل الدخول؛ وإذا تشارك 200 مستخدم افتراضي حساباً واحداً، تشاركوا سلة واحدة. كلا التشغيلين يُنتج رسوماً بيانية، ولا يقيس أيٌّ منهما إتمام الشراء.
لماذا يهم
نادراً ما يفشل السكربت المعطوب فشلاً ظاهراً. فالطلبات المرفوضة تعود في أجزاء من الألف من الثانية، وقد تصل صفحات الخطأ بالحالة 200 OK، والتشغيل الذي رُفضت معظم طلباته قد يُظهر أزمنة استجابة أفضل من النظام الحقيقي. وعندها تُبنى قرارات الإصدار والسعة على حمل لم يحدث قط.
وبالنسبة إلى المختبِر، فكتابة السكربت هي الموضع الذي يلتقي فيه نموذج عبء العمل بالنظام الحقيقي: أيّ القيم يُصدرها الخادم لكل جلسة، وأيّ بيانات اختبار تستهلكها كل رحلة، وكيف تُعرف الاستجابة الصحيحة. وهذه الإجابات تحدد هل يمكن الوثوق بالنتائج.
التسجيل أو الكتابة، والمعاملات والمؤقّتات
يمكنك أن تسجّل السكربت أو تكتبه. تسجيل الرحلة (Recording) يلتقط حركة الشبكة (Traffic) بينما تنقر عبر خطواتها: فمسجّل JMeter المسمّى HTTP(S) Test Script Recorder يعمل وسيطاً (Proxy) بين المتصفح والخادم، وتوفّر Grafana محوّلاً من تسجيل المتصفح (ملف HAR) إلى سكربت k6. والتسجيل سريع لكنه يلتقط كل شيء: استدعاءات التحليلات، وموارد أطراف ثالثة لا يحق لك اختبار حملها، وكل قيمة أصدرها الخادم ذلك اليوم. أما الكتابة من عقد الواجهة البرمجية أو من لوحة الشبكة في المتصفح فتعطي طلبات أقل وأنظف، وهي المعتادة للواجهات البرمجية؛ وكثير من المختبِرين يسجّلون أولاً ليروا ما يُرسَل، ثم يعيدون الكتابة.
وفي الحالتين، ابنِ السكربت من المعاملات (Transactions): مجموعات مسمّاة من الطلبات تشكّل خطوة واحدة للمستخدم، مثل «تسجيل الدخول»، يُقاس زمنها معاً مقابل أهداف زمن الاستجابة. يستخدم JMeter لذلك Transaction Controller، ويستخدم k6 الدالة group(). وتضيف المؤقّتات (Timers) زمن التفكير بين المعاملات، حتى لا يُحسب التوقف زمنَ استجابة. وخطة JMeter أدناه تُظهر كل الأجزاء المستخدمة في هذا الدرس.
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}ربط القيم الديناميكية والتغذية بالبيانات
ربط القيم الديناميكية (Correlation) يعني التقاط قيمة متغيرة من استجابة وإرسالها في طلب لاحق: معرّفات الجلسات؛ وقيم CSRF، وهي رموز مكافحة التزوير التي يضعها الخادم في حقل مخفي من النموذج أو في جسم JSON وينتظر عودتها مع كل تغيير؛ ومعرّفات ما أنشأه السكربت مثل طلب الشراء. والسكربت المسجَّل يحمل قيم يوم التسجيل، وقد انتهت صلاحيتها أو صارت تخص جلسة أخرى. وملفات تعريف الارتباط الخاصة بالجلسة هي الحالة السهلة: فمدير ملفات تعريف الارتباط في JMeter (HTTP Cookie Manager) ووعاء ملفات تعريف الارتباط (Cookie Jar) في k6 يحفظانها ويعيدان إرسالها لكل مستخدم افتراضي كما يفعل المتصفح. أما القيم الأخرى في أجسام الطلبات والترويسات والروابط فتربطها بنفسك، بأربع خطوات.
والتغذية بالبيانات (Parameterisation) تستبدل القيم الثابتة بمتغيرات من ملف بيانات، فيصبح كل مستخدم افتراضي شخصاً مختلفاً له حسابه ومنتجه وحمولة طلبه (Payload)، مثل عبارة بحث أو عنوان توصيل. فالحساب المشترك يعني جلسة واحدة وسلة واحدة وصفوف قاعدة البيانات نفسها للجميع، ومعرّف المنتج الواحد يعني أن كل صفحات المنتج قد تأتي من ذاكرة التخزين المؤقت (Cache). والبيانات التي تستهلكها الرحلة، مثل عناوين البريد للتسجيل في الموقع أو القسائم التي تُستخدم مرة واحدة، تحتاج إلى صف لكل تكرار (Iteration) لا لكل مستخدم. يقرأ JMeter الملف عبر CSV Data Set Config، ويقرؤه k6 بالدالة open() مع SharedArray.
- البحث: سجّل الرحلة مرتين، أو بحسابين، وقارن؛ فالقيم التي تختلف وجاءت من استجابة سابقة هي المرشحة.
- الاستخراج: مباشرةً بعد الاستجابة التي تُصدر القيمة، بمسار JSON أو تعبير نمطي (Regular Expression) أو محدِّد CSS أو بالنص المحيط بها (Boundaries)؛ ويقرأ k6 القيمة بالدالة
res.json()أو بالبحث في جسم الاستجابة. - الاستبدال: استخدم المتغير في كل موضع ظهرت فيه القيمة المسجّلة، في أجسام الطلبات والترويسات والروابط.
- التحقق: افحص أن الاستخراج وجد شيئاً؛ فالقيمة الافتراضية مثل
NOT_FOUNDتُظهر الفشل بدلاً من إرسال قيمة فارغة.
users.csv والقيمة الجديدة من تسجيل دخوله.التسجيل يُعاد كما هو
- المستخدم الافتراضي سكربت k6
- الخادم
staging.example
- 1 من المستخدم الافتراضي إلى الخادم: تسجيل الدخول
POST /api/login - 2 من الخادم إلى المستخدم الافتراضي: جلسة جديدة وقيمة CSRF جديدة ردّ
{"csrfToken":"f3a9…"} - 3 من المستخدم الافتراضي إلى الخادم: يرسل قيمة يوم التسجيل
X-CSRF-Token: 5d0b… - 4 من الخادم إلى المستخدم الافتراضي: مرفوض ردّ
403 Forbiddenفشل
النتيجة: يفشل كل طلب بعد الدخول، وبسرعة. فشل
بعد الربط والتغذية بالبيانات
- ملف البيانات
users.csv - المستخدم الافتراضي سكربت k6
- الخادم
staging.example
- 1 من ملف البيانات إلى المستخدم الافتراضي: صفّه الخاص: الحساب والمنتج
lt.user0007@…, SKU-10482 - 2 من المستخدم الافتراضي إلى الخادم: تسجيل الدخول بذلك الحساب
POST /api/login - 3 من الخادم إلى المستخدم الافتراضي: ملف تعريف ارتباط الجلسة وقيمة CSRF جديدة ردّ
Set-Cookie: sid=Q7mT… {"csrfToken":"f3a9…"} - 4 المستخدم الافتراضي داخليًا: استخراج القيمة وفحصها إن غابت ينتهي التكرار
csrf = res.json('csrfToken') - 5 المستخدم الافتراضي داخليًا: زمن التفكير، عشوائي من 3 إلى 7 ثوانٍ
- 6 من المستخدم الافتراضي إلى الخادم: إضافة منتج الصف مع إرسال القيمة يضيف الوعاء ملف تعريف ارتباط الجلسة
X-CSRF-Token: f3a9… - 7 من الخادم إلى المستخدم الافتراضي: سطر السلة لهذا المستخدم ردّ
201 {"sku":"SKU-10482",…} - 8 المستخدم الافتراضي داخليًا: الفحص: الحالة 201 والرمز SKU نفسه نجاح
النتيجة: لا يرسل كل مستخدم افتراضي إلا القيم التي صدرت له في هذا التكرار. نجاح
- ملف البيانات
النسخة النصية
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جرّبها: مولّد بيانات اختبار التحميل جرّبها: مولّد CSV لـ JMeter جرّبها: مولّد مجموعات بيانات CSV
زمن التفكير والإيقاع وفحوص الاستجابة
أعطاك نموذج عبء العمل زمن التفكير (Think Time) والإيقاع (Pacing)؛ وفي السكربت لا يأخذان إلا بضعة أسطر. توقّف عشوائياً بين الخطوات: sleep(3 + Math.random() * 4) في k6، أو المؤقّت Uniform Random Timer في JMeter. وللإيقاع، اترك للأداة ضبط المعدّل (منفّذات معدّل الوصول في k6، والمؤقّت Constant Throughput Timer في JMeter)، أو توقّف بقية الفاصل الزمني لكل تكرار. فبلا توقف يُرسل بضعة مستخدمين افتراضيين حركة أشخاص كثيرين، ولا يعود الاختبار يطبّق الحمل الذي وصفه النموذج.
تقيس أداة الحمل زمن كل استجابة أياً كان محتواها، فبلا فحوص يُحسب الخطأ السريع نجاحاً سريعاً. ورموز الحالة 4xx و5xx تُكتشف افتراضياً: يَسِم JMeter العيّنة (Sample)، أي سجلّه لطلب واحد، بالفشل، ويحسبها k6 في http_req_failed. لكن إخفاقات كثيرة تصل بحالة نجاح: صفحة تسجيل الدخول بعد ضياع الجلسة، أو صفحة خطأ بالحالة 200 OK، أو جسم يقول "status":"error"، أو نتيجة بحث فارغة بسبب بيانات خاطئة. فافحص (Check) في كل استجابة حالتها وشيئاً لا تحتويه إلا الاستجابة الصحيحة.
- افحص محتوى يثبت أن الخطوة نجحت، مثل قيمة حقل أو اسم المنتج، لا نصاً يظهر في كل صفحة.
- اجعل الفحوص الفاشلة مؤثّرة: في k6 لا يُفشل
check()الفاشل التشغيلَ إلا عبر عتبة (Threshold) مثلchecks: ['rate>0.99']؛ وفي JMeter يَسِم التأكيد (Assertion) الفاشل العيّنة بالفشل. - أنهِ الرحلة المعطوبة: إذا فشل تسجيل الدخول فأوقف التكرار بدلاً من إرسال الخطوات الباقية بقيمة فارغة، لكن أبقِ التوقف، وإلا أعاد المستخدم الفاشل محاولة الدخول بأقصى سرعة.
مخطط الحمل والتحقق بمستخدم واحد وسعة مولّد الحمل
مخطط الحمل (Load Profile) إعدادات وليس شيفرة الرحلة. وله عادةً ثلاث مراحل: الصعود التدريجي (Ramp-up) يضيف المستخدمين الافتراضيين شيئاً فشيئاً، والحالة المستقرة (Steady State) تُبقي الحمل المستهدف ثابتاً أثناء القياس، والهبوط التدريجي (Ramp-down) يُخرجهم. في k6 تكون كل مرحلة عنصراً في stages، أو تختار منفّذ سيناريو؛ وفي مجموعة خيوط JMeter تحدد عدد الخيوط (Threads)، وكل خيط مستخدم افتراضي واحد، وفترة الصعود التدريجي، والمدة، وهي تشمل فترة الصعود (والمجموعة القياسية لا تدعم الهبوط التدريجي). واختيار المخطط موضوع الدرس التالي عن اختبار الحمل. أبقِ المخطط والعنوان الأساسي وملف البيانات خارج شيفرة الرحلة، ليعمل السكربت نفسه بمستخدم واحد أو بمستخدمين كثيرين.
قبل أي حمل، شغّل مستخدماً افتراضياً واحداً لتكرار واحد مع سجلّ تفصيلي كامل للطلبات والاستجابات (Full Logging): View Results Tree في JMeter، أو --http-debug=full في k6. اقرأ كل طلب واستجابة، وتأكد أن كل قيمة مستخرجة جديدة وأن طلب الشراء موجود للمستخدم والمنتج الصحيحين. كرّر بضعة تكرارات وسجّل زمن كل معاملة: هذا خط الأساس لمستخدم واحد (Single-user Baseline) الذي تُقارن به نتائج الحمل، والخطوة البطيئة لمستخدم واحد نتيجةٌ تستحق الإبلاغ منذ الآن. ثم شغّل بضعة مستخدمين لبضع دقائق لكشف الحسابات المشتركة والبيانات المتصادمة ونفاد الصفوف.
- لمولّد الحمل (Load Generator) حدود: إذا تشبّع معالجه أو ذاكرته أو شبكته اصطفّت الطلبات داخله، وقاس التشغيلُ المولّدَ لا النظام. راقب موارده، وشغّل JMeter دون الواجهة الرسومية (Non-GUI Mode) ودون مستمعات التصحيح مثل View Results Tree، وفعّل في k6 الخيار
discardResponseBodiesواطلبresponseType: 'text'فقط في الطلبات التي يقرأ فحصٌ جسم استجابتها. - إذا لم يكفِ جهاز واحد فوزّع الحمل وأعطِ كل مولّد شريحته من ملف البيانات، وإلا سجّل مولّدان الدخول بالمستخدمين أنفسهم.
مثال تطبيقي: بداية سكربت إتمام الشراء في k6
في بيئة الاختبار لمتجر إلكتروني 500 حساب اختبار في الملف users.csv، وفي كل صف بريد إلكتروني وكلمة مرور ورمز منتج (SKU). يغطي السكربت أدناه أول خطوتين من إتمام الشراء، والخطوات الباقية تتبع النمط نفسه. أما ملف تعريف الارتباط الخاص بالجلسة فينتقل في وعاء k6 دون أي شيفرة.
- البيانات: تحمّل
SharedArrayالملف مرة واحدة لكل المستخدمين الافتراضيين، ويعطي__VU(1 و2 و3 …) كلاً منهم صفّه: 200 مستخدم، 200 حساب. والتقسيم البسيط بـsplitيفترض قيماً بلا فواصل ولا علامات اقتباس. - ربط القيم: تذهب
csrfTokenالمأخوذة من استجابة تسجيل الدخول إلىX-CSRF-Token؛ وإذا غابت فشل فحص وانتهى التكرار بعد توقف. - العتبات: يفشل التشغيل إذا فشل 1% من الفحوص أو أكثر، أو بلغ p95 لخطوة السلة 800 ms.
- المخطط: 5 دقائق من الصعود التدريجي إلى 200 مستخدم، و30 دقيقة من الحالة المستقرة، و3 دقائق من الهبوط التدريجي. وتحقق أولاً بالأمر
k6 run --vus 1 --iterations 1 --http-debug=full -e BASE_URL=https://staging.example checkout.js.
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();
}أخطاء شائعة
- إعادة تشغيل التسجيل كما هو: فقيم الجلسة وCSRF القديمة تجعل كل طلب بعد الدخول مرفوضاً، واستدعاءات الأطراف الثالثة المسجّلة تحمّل خدمات لا يحق لك اختبارها.
- تشغيل كل المستخدمين الافتراضيين بحساب واحد أو منتج واحد، فتتصادم الجلسات وتجيب ذاكرة التخزين المؤقت عن كل شيء.
- الاكتفاء برموز الحالة، فتبدو إعادات التوجيه إلى صفحة الدخول وصفحات الخطأ بالحالة
200 OKنجاحات سريعة. - إهمال زمن التفكير، أو التوقف داخل المعاملة حيث قد يُحسب التوقف ضمن زمنها: فالدالة
group()في k6 تحسبه دائماً. - البدء بالحمل الكامل دون تشغيل بمستخدم واحد، فتُدفن أخطاء السكربت في آلاف الإخفاقات.
- تجاهل مولّد الحمل: فالجهاز المتشبّع، أو JMeter بالواجهة الرسومية مع مستمعات التصحيح، يُظهر النظام أبطأ مما هو عليه.
من منظور ضمان الجودة
عامل السكربت بوصفه جزءاً من مواد الاختبار (Testware) يحتاج إلى مراجعة. اسأل المطورين عن القيم التي تُصدر لكل جلسة أو لكل طلب وعن الطلبات التي ترسلها الرحلة فعلاً؛ وتأكد أن في بيئة الاختبار ما يكفي من حسابات الاختبار والمنتجات وبدائل الدفع (Payment Stubs)؛ واتفق مع مالك المنتج على شكل الاستجابة الصحيحة لكل خطوة، حتى تختبر الفحوص نتائج الأعمال لا رموز الحالة وحدها.
احفظ السكربت في نظام التحكم بالإصدارات مع مولّد بياناته وملاحظة قصيرة: إصدار التطبيق، والقيم المربوطة، وملفات البيانات، وأمر التشغيل بمستخدم واحد. وسجّل خط الأساس لمستخدم واحد مع رقم البناء (Build)، وأعد التشغيل بمستخدم واحد بعد كل إصدار: فحقل أُعيدت تسميته أو قيمة أمان جديدة تكسر الربط بصمت.
تمرين عملي
اعثر على القيم الديناميكية في سكربت حجز مسجَّل
سجّلت رحلة حجز موعد في عيادة بحساب اختبار واحد، والتسجيل المختصر أدناه. حدّد القيم التي يجب ربطها والقيم التي يجب تغذيتها من ملف بيانات، واكتب فحصاً واحداً لكل استجابة، وبيّن كيف ستتحقق من السكربت قبل اختبار حمل بـ100 مستخدم.
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"}- علّم كل قيمة أصدرها الخادم، والاستجابة التي جاءت منها، وطريقة استخراجها.
- علّم القيم التي يجب أن تختلف بين المستخدمين الافتراضيين، وملف البيانات الذي يزوّدها.
- اكتب لكل استجابة فحصاً واحداً لا تجتازه إلا الاستجابة الصحيحة.
- خطّط للتشغيل بمستخدم واحد ثم بعدد قليل من المستخدمين.
إظهار الإجابة النموذجية
الربط: ملف تعريف الارتباط sid من الاستجابتين 1 و2 (يتولاه مدير ملفات تعريف الارتباط أو الوعاء؛ تأكد أن المعرّف يتغيّر عند الدخول)؛ و_csrf من الحقل المخفي في الاستجابة 1 (بمحدِّد CSS أو تعبير نمطي)، ويُرسل في الطلب 2 وفي X-CSRF-Token في الطلب 4، أو القيمة الأحدث إن أصدر تسجيل الدخول قيمة جديدة؛ وslotId من الاستجابة 3 (بمسار JSON، موعد متاح لا الأول دائماً)؛ ومعرّف الحجز من الترويسة Location في الاستجابة 4، للطلب 5.
التغذية بالبيانات: البريد الإلكتروني وكلمة المرور، حساب اختبار لكل مستخدم افتراضي؛ وpatientId الذي لا تُظهره أي استجابة، في صف بيانات الحساب؛ والعيادة والتاريخ، على أن يكون التاريخ دائماً في المستقبل بدلاً من القيمة الثابتة المسجّلة 2026-11-03، موزّعَين حتى لا يتنافس 100 مستخدم على الموعدين أنفسهما، إلا إذا كان هذا التنافس هو موضوع الاختبار.
الفحوص: (1) قيمة _csrf في النموذج؛ (2) إعادة توجيه 303 إلى /home لا العودة إلى /login (وإذا كانت الأداة تتبع إعادة التوجيه فافحص الرابط النهائي)؛ (3) مصفوفة JSON فيها موعد واحد على الأقل؛ (4) 201 مع ترويسة Location؛ (5) "status":"confirmed" والوقت المحجوز.
التحقق: مستخدم واحد، وتكرار واحد، وسجلّ تفصيلي كامل؛ تأكد أن القيم جديدة وأن الحجز موجود لذلك المريض، ثم بضعة تكرارات لخط الأساس. بعدها 5 مستخدمين لبضع دقائق لكشف الحسابات المشتركة وتصادم المواعيد ونفاد البيانات، مع إعادة المواعيد بين التشغيلات. وبعد ذلك فقط 100 مستخدم، مع مراقبة مولّد الحمل.
أسئلة شائعة
هل أستخدم JMeter أم k6 لأول سكربت؟
كلاهما يعلّم المهارات نفسها. يبني JMeter خطة الاختبار في واجهة رسومية من عناصر جاهزة للتسجيل والاستخراج وملفات البيانات، ويُشغَّل من سطر الأوامر للاختبار الفعلي. أما سكربتات k6 فملفات JavaScript تُحفظ في نظام التحكم بالإصدارات وتناسب خط CI. استخدم ما يشغّله فريقك أصلاً؛ فربط القيم والتغذية بالبيانات والفحوص والتحقق تعمل بالطريقة نفسها في الأداتين.
هل أختبر الحمل بسكربت أتمتة متصفح مثل Selenium أو Playwright؟
ليس على نطاق واسع. فكل متصفح يستهلك من المعالج والذاكرة أكثر بكثير من مستخدم افتراضي على مستوى البروتوكول، فلا يشغّل الجهاز الواحد منها إلا عدداً أقل بكثير. أما سكربتات مستوى البروتوكول فترسل طلبات HTTP مباشرةً. وتفيد الاختبارات على مستوى المتصفح إلى جانب حمل البروتوكول لقياس ما يعيشه مستخدم حقيقي واحد في الصفحة، مثل زمن العرض، بينما النظام تحت الحمل.
كم صفاً من بيانات الاختبار أحتاج؟
صفاً واحداً على الأقل لكل مستخدم افتراضي للبيانات التي تعرّف المستخدم، مثل الحسابات. وللبيانات التي تستهلكها الرحلة، مثل التسجيلات الجديدة أو القسائم التي تُستخدم مرة واحدة، اضرب عدد المستخدمين الافتراضيين في عدد التكرارات التي يُكملها كلٌّ منهم في التشغيل، وأضف هامشاً. أما بيانات القراءة المتنوعة، مثل معرّفات المنتجات، فيجب أن تكون كبيرة بما يكفي لتتصرف ذاكرة التخزين المؤقت كما في الإنتاج، وهو ما يحدده نموذج عبء العمل.
المراجع
- Certified Tester Performance Testing (CT-PT)منهج ISTQB التخصصي لاختبار الأداء: المفاهيم الأساسية وقياسات الأداء وأنشطة الاختبار ومهامه والأدوات.
- Grafana k6 documentationالتوثيق الرسمي لأداة Grafana k6، وهي أداة مفتوحة المصدر وقابلة للتوسعة لاختبار الحمل والأداء.
- Apache JMeter User's Manualكيف تُبنى اختبارات الحمل وتُعامَل بالمعاملات وتُشغَّل في JMeter، وكيف تُقرأ مستمعاته وتقاريره.
- ISTQB Glossaryالتعريفات القياسية لمصطلحات الاختبار — حالة الاختبار واختبار الانحدار والخطورة والأولوية وتحليل القيم الحدّية وغيرها.
تدرّب بأدوات متقن
ولّد مجموعات معاملات كبيرة (مستخدمين ورموزاً ومعرّفات وحمولات) لاختبارات التحميل في k6 وJMeter وGatling وLocust.
ولّد ملفات CSV لعنصر CSV Data Set Config في JMeter باقتباس وفواصل وترميزات صحيحة.
ولّد ملفات CSV بأعمدة وأنواع بيانات وأعداد صفوف وفواصل مخصصة.
ولّد سكربتات اختبار التزامن (k6 وJMeter JMX وLocust وArtillery وNode.js) بإعدادات افتراضية آمنة لنقطة نهاية مستهدفة.
ولّد مدخلات اختبار الأداء بتوزيعات واقعية: أزمنة التفكير، ومزيج الطلبات، وأحجام الحمولات، ومنحنيات التدرّج.
قِس زمن استجابة نقطة نهاية عبر عدة عيّنات: الحد الأدنى والأقصى والمتوسط والوسيط وp95.
للتعمّق أكثر
تقدّمك في هذا الموضوع
سجّل الدخول لتتابع تقدّمك
علّم الموضوعات مكتملة، وتابع من حيث توقفت، وافتح امتحانات التخصص.