كيفية استخدام Assert في Node.js: دليل عملي

INEZA Felin-Michel

INEZA Felin-Michel

22 مايو 2026

كيفية استخدام Assert في Node.js: دليل عملي

Apidog للمؤسسات

نشر محلي

SSO & RBAC

متوافق مع SOC 2

استكشاف Apidog Enterprise

كل إطار عمل للاختبار استخدمته، سواء كان Jest أو Mocha أو node:test، مبني على فكرة بسيطة: تحديد ما تتوقعه، ثم إلقاء خطأ إذا اختلفت الحقيقة. يقوم Node.js بتضمين هذه الفكرة كوحدة مدمجة تسمى assert. لا يوجد تثبيت ولا تبعيات، فقط قم بـ require لها وابدأ في التحقق من الافتراضات.

وحدة assert تستحق المعرفة بحد ذاتها. إنها تدعم فحوصات السلامة السريعة في السكربتات، وتعد أساسًا للعديد من أدوات تشغيل الاختبار، وتعلمك ما هو التأكيد فعليًا قبل أن يقوم أي إطار عمل بتزيينه. يغطي هذا الدليل الطرق المهمة، والتمييز بين الوضع الصارم والقديم الذي يربك الناس، وكيفية التأكيد على الأخطاء والكود غير المتزامن، وكيف تساعدك نفس الوحدة في التحقق من صحة استجابات واجهة برمجة التطبيقات (API).

ما الذي تفعله وحدة assert

التأكيد هو عبارة يجب أن تكون صحيحة لكي يعتبر برنامجك سليمًا. عندما تكتب assert.strictEqual(total, 100)، فأنت تعلن أن total يجب أن يساوي 100. إذا كان كذلك، فلن يحدث شيء ويستمر التنفيذ. وإذا لم يكن كذلك، فإن assert يلقي خطأ AssertionError يوقف التنفيذ ويخبرك بما حدث خطأ.

قم باستيراد الوحدة. الشكل الحديث والموصى به هو الإصدار الصارم:

const assert = require('node:assert/strict');
// or with ES modules:
// import assert from 'node:assert/strict';

أبسط تأكيد يتحقق مما إذا كانت القيمة حقيقية (truthy):

const user = getUser(42);
assert(user, 'getUser should return a user object');

الوسيط الأول هو القيمة قيد الاختبار. الوسيط الثاني، وهو اختياري، هو رسالة تظهر عند فشل التأكيد. اكتب هذه الرسالة دائمًا. إن فشلاً يقول “getUser should return a user object” هو أكثر فائدة بكثير من AssertionError مجرد. يساعد فهم التأكيدات أيضًا عند الانتقال إلى تأكيدات واجهة برمجة التطبيقات (API) المخصصة في أداة الاختبار.

الوضع الصارم مقابل الوضع القديم

هذا هو أهم شيء يجب فهمه بشكل صحيح. تحتوي وحدة assert على وضعين.

الوضع القديم، الذي تحصل عليه من require('node:assert')، يستخدم المساواة غير الصارمة (==) لـ assert.equal و assert.deepEqual. وهذا يعني أن assert.equal(1, '1') ينجح، لأن 1 == '1' صحيح في JavaScript. المساواة غير الصارمة هي مصدر معروف للأخطاء.

الوضع الصارم، الذي تحصل عليه من require('node:assert/strict')، يستخدم المساواة الصارمة (===) لكل شيء. assert.equal(1, '1') يفشل، كما يجب، لأن الأنواع مختلفة.

const looseAssert = require('node:assert');
looseAssert.equal(1, '1');        // passes, surprising and dangerous

const strict = require('node:assert/strict');
strict.equal(1, '1');             // throws AssertionError, correct

استخدم الوضع الصارم. لا يوجد سبب وجيه لقبول المساواة غير الصارمة في الاختبارات. يفترض باقي هذا الدليل استخدام node:assert/strict، حيث يتصرف assert.equal تمامًا مثل assert.strictEqual.

مقارنة القيم: equal و strictEqual

assert.strictEqual(actual, expected) يتحقق من أن قيمتين متطابقتان باستخدام ===. إنها الأداة الأساسية للأنواع البدائية مثل الأرقام والسلاسل المنطقية والقيم المنطقية.

const assert = require('node:assert/strict');

function priceWithTax(price, rate) {
  return price + price * rate;
}

assert.strictEqual(priceWithTax(100, 0.08), 108, 'tax calc should add 8 percent');
assert.strictEqual(typeof priceWithTax(100, 0.08), 'number', 'result should be a number');

يوجد أيضًا assert.notStrictEqual، والذي ينجح عندما لا تكون القيمتان متطابقتين. استخدمه لتأكيد أن القيمة قد تغيرت:

const before = getCacheKey();
refreshCache();
const after = getCacheKey();
assert.notStrictEqual(before, after, 'cache key should change after refresh');

بالنسبة للكائنات والمصفوفات، لن يكون strictEqual مفيدًا. فكائناً حرفيان بنفس المحتويات هما إشارتان مختلفتان، لذا فإن === يعيد false. هذا هو الغرض من المساواة العميقة.

مقارنة الكائنات: deepStrictEqual

assert.deepStrictEqual(actual, expected) يقارن البنية والقيم بشكل متكرر. ينجح كائنان إذا كان كل مفتاح يحمل قيمة متساوية تمامًا، وصولاً إلى أعمق المستويات.

const assert = require('node:assert/strict');

function buildOrder(id, items) {
  return { id, items, status: 'pending' };
}

assert.deepStrictEqual(
  buildOrder(7, ['keyboard', 'mouse']),
  { id: 7, items: ['keyboard', 'mouse'], status: 'pending' },
  'order object should match expected shape'
);

هذه هي الطريقة التي ستستخدمها غالبًا عند اختبار الدوال التي تُرجع كائنات، وخاصة عند اختبار استجابات واجهة برمجة التطبيقات (API)، حيث أن هياكل JSON هي كائنات. نظيرها assert.notDeepStrictEqual ينجح عندما تختلف الهياكل.

تنبيه واحد: deepStrictEqual يتحقق من الأنواع أيضًا. الخاصية التي تحمل الرقم 7 لن تتطابق مع الخاصية التي تحمل السلسلة النصية '7'. هذه الصرامة هي ميزة؛ فهي تكتشف انحراف النوع في بياناتك. إذا كنت تختبر دوالًا ذات تركيبات إدخال متعددة، يوضح دليلنا حول الاختبار القائم على البيانات باستخدام CSV و JSON كيفية توسيع نطاق التأكيدات عبر مجموعات البيانات.

عندما يفشل deepStrictEqual، يطبع Node فرقًا يسلط الضوء بالضبط على الخصائص التي لا تتفق. هذا الفرق هو أحد أكثر الأشياء فائدة في الوحدة. بدلاً من التحديق في كائنين كبيرين محاولًا اكتشاف الفرق، تقرأ بضعة أسطر مميزة. بالنسبة للبيانات المتداخلة، هذا وحده يبرر استخدام deepStrictEqual بدلاً من التحقق اليدوي من كل حقل باستخدام strictEqual.

المطابقة الجزئية باستخدام assert.match و assert.ok

ليس كل فحص يتطلب مساواة كاملة. أحيانًا تهتم فقط بأن تبدو القيمة صحيحة تقريبًا. ينجح assert.ok(value) كلما كانت القيمة حقيقية (truthy)، وهي الأداة الصحيحة لفحوصات "يجب أن يكون هذا موجودًا": سلسلة غير فارغة، كائن معرف، عدد غير صفري.

assert.match(string, regexp) يتحقق من أن سلسلة نصية تتطابق مع تعبير عادي (regular expression)، و assert.doesNotMatch يتحقق من العكس. هذه مفيدة للقيم التي يختلف محتواها الدقيق ولكن شكلها ثابت.

const assert = require('node:assert/strict');

function generateOrderId() {
  return 'ORD-' + Date.now();
}

const id = generateOrderId();

// the exact timestamp changes, so assert the format, not the value
assert.match(id, /^ORD-\d+$/, 'order id should be ORD- followed by digits');
assert.ok(id.length > 4, 'order id should not be empty');

هذا النمط مهم بشكل خاص لاختبار واجهة برمجة التطبيقات (API)، حيث تحتوي الاستجابات على طوابع زمنية، ومعرفات تم إنشاؤها، ورموز لا يمكنك التنبؤ بها. يمكنك تأكيد التنسيق باستخدام match والوجود باستخدام ok، وتحتفظ بـ strictEqual للقيم التي تتحكم فيها فعليًا.

التأكيد على أن الكود يلقي خطأ

أحيانًا يعني السلوك الصحيح إلقاء خطأ. assert.throws(fn, expectedError) يشغل دالة وينجح فقط إذا ألقت خطأ.

const assert = require('node:assert/strict');

function parsePort(value) {
  const port = Number(value);
  if (!Number.isInteger(port) || port < 1 || port > 65535) {
    throw new RangeError('port must be an integer between 1 and 65535');
  }
  return port;
}

// passes: invalid input should throw a RangeError
assert.throws(
  () => parsePort('70000'),
  RangeError,
  'out-of-range port should throw RangeError'
);

// you can also match the error message with a regex
assert.throws(
  () => parsePort('abc'),
  /must be an integer/,
  'non-numeric port should throw a descriptive error'
);

// passes: valid input should not throw
assert.doesNotThrow(
  () => parsePort('8080'),
  'a valid port should not throw'
);

لاحظ أنك تمرر دالة، وليس استدعاءً. assert.throws(parsePort('70000')) سيقوم بتنفيذ parsePort قبل أن تراه assert على الإطلاق، وسيهرب الخطأ دون أن يتم التقاطه. قم دائمًا بتغليفه: () => parsePort('70000').

التأكيد على الكود غير المتزامن: rejects

كود Node.js الحديث مليء بالوعود (promises)، والوعد المرفوض هو المعادل غير المتزامن للخطأ الملقى. يتعامل assert.rejects و assert.doesNotReject مع هذه الحالة. كلاهما يُرجع وعودًا، لذا يجب عليك await انتظارها.

const assert = require('node:assert/strict');

async function fetchUser(id) {
  if (typeof id !== 'number') {
    throw new TypeError('id must be a number');
  }
  // ... real lookup would go here
  return { id, name: 'Dana Lee' };
}

async function runTests() {
  // passes: bad input rejects with a TypeError
  await assert.rejects(
    fetchUser('not-a-number'),
    TypeError,
    'string id should reject'
  );

  // passes: good input resolves without rejecting
  await assert.doesNotReject(
    fetchUser(101),
    'valid id should resolve cleanly'
  );
}

runTests();

نسيان await عند استدعاء assert.rejects هو خطأ شائع. بدون await، تنتهي دالة الاختبار قبل أن يتم تسوية التأكيد، ويصبح الفشل رفضًا غير معالج بدلاً من خطأ اختبار واضح.

استخدام assert لاختبار استجابات واجهة برمجة التطبيقات (API)

وحدة assert مفيدة حقًا للتحقق من مخرجات طلب HTTP. بعد جلب نقطة نهاية (endpoint)، يكون لديك رمز حالة (status code) وهيكل JSON (JSON body)، وكلاهما أشياء يمكن التأكيد عليها.

const assert = require('node:assert/strict');

async function testGetUser() {
  const res = await fetch('https://api.example.com/users/101');

  // check the status code
  assert.strictEqual(res.status, 200, 'GET /users/101 should return 200');

  const body = await res.json();

  // check the shape and types of the response
  assert.strictEqual(typeof body.id, 'number', 'id should be a number');
  assert.strictEqual(body.id, 101, 'id should match the requested user');
  assert.ok(body.name, 'response should include a name');
  assert.ok(Array.isArray(body.roles), 'roles should be an array');
}

testGetUser().then(
  () => console.log('API test passed'),
  (err) => { console.error('API test failed:', err.message); process.exitCode = 1; }
);

يعمل هذا النمط، وهو يعلم الأساسيات. ولكن بالنسبة لمجموعة اختبار API حقيقية، ستحتاج إلى عمليات تشغيل منظمة، وإعادة محاولة، ومعالجة البيئة، وتقارير لا توفرها assert الخام. تغطي أدلةنا حول كيفية كتابة سكربتات الاختبار الآلية و إطار عمل أتمتة API Pytest هذه الخطوة التالية.

إذا كنت لا ترغب في بناء إطار اختبار يدويًا، فإن Apidog يتيح لك تحديد طلبات واجهة برمجة التطبيقات (API) وإرفاق تأكيدات مرئية على رموز الحالة والرؤوس وحقول JSON دون كتابة أي كود تأكيد على الإطلاق. يمكنك ربط الطلبات في سيناريوهات اختبار آلية، وتشغيلها في CI/CD، ولا يزال بإمكانك الانتقال إلى السكربتات المخصصة عندما تحتاج إلى التحكم الذي توفره لك assert. إنه مسار أسرع من "لدي نقطة نهاية" إلى "لدي مجموعة اختبار"، ويمكنك تنزيل Apidog واستخدامه مجانًا. جنبًا إلى جنب مع Apidog، تغطي وحدة assert في Node.js كلاً من الفحوصات السريعة ومجموعات الاختبار الكاملة.

الأسئلة المتكررة

هل أحتاج إلى تثبيت وحدة assert؟

لا. assert مدمجة في Node.js. قم باستيرادها باستخدام require('node:assert/strict') أو require('node:assert'). لا توجد حزمة npm لتثبيتها ولا توجد تبعية لإدارتها. البادئة node: تجعل من الواضح أنك تقوم بتحميل وحدة أساسية.

ما الفرق بين assert.equal و assert.strictEqual؟

في الوضع القديم، يستخدم assert.equal المساواة غير الصارمة (==) ويستخدم assert.strictEqual المساواة الصارمة (===). في الوضع الصارم، الذي يتم تحميله عبر node:assert/strict، يتصرف assert.equal بشكل مماثل لـ assert.strictEqual. استخدم الوضع الصارم دائمًا حتى لا يتسبب تحويل النوع في اجتياز اختبار بصمت.

متى يجب أن أستخدم deepStrictEqual بدلاً من strictEqual؟

استخدم strictEqual للأنواع البدائية: الأرقام، السلاسل النصية، القيم المنطقية. استخدم deepStrictEqual للكائنات والمصفوفات، لأن strictEqual يقارن المراجع وكائنين منفصلين لا يكونان متساويين مرجعيًا أبدًا. في أي وقت تقوم فيه بالتأكيد على هيكل JSON أو كائن مُعاد، استخدم deepStrictEqual.

هل يجب أن أستخدم وحدة assert أم إطار عمل للاختبار مثل Jest؟

للسكربتات السريعة والتعلم، assert الخام جيد. لمجموعة اختبار حقيقية، استخدم إطار عمل. يوفر Node.js أيضًا أداة تشغيل اختبار مدمجة، node:test، تتوافق بشكل طبيعي مع assert وتوفر لك تنظيم الاختبار، وإعداد التقارير، والتوازي دون تبعية خارجية.

كيف يمكنني التأكيد على أن دالة غير متزامنة (async function) ترفض (rejects)؟

استخدم assert.rejects، وتذكر أن تقوم بـ await لها. مرر الوعد (promise) أو استدعاء دالة غير متزامنة (async function)، اختياريًا مع نوع خطأ متوقع أو تعبير عادي للرسالة. assert.rejects(somePromise, TypeError) ينجح فقط إذا رفض الوعد بـ TypeError. بدون await، يصبح الفشل رفضًا غير معالج بدلاً من خطأ تأكيد واضح.

ممارسة تصميم API في Apidog

اكتشف طريقة أسهل لبناء واستخدام واجهات برمجة التطبيقات