ویژگی تصویر

استفاده از Mocha و Chai در Node.js — راهنمای جامع

  /  Node.js   /  استفاده از Mocha و Chai در Node.js
بنر تبلیغاتی الف
NodeJS - Node.js

تست واحد (Unit Testing) در توسعهٔ نرم‌افزار نقش کلیدی در افزایش کیفیت، کاهش باگ و تسهیل توسعهٔ افزایشی دارد. در اکوسیستم Node.js، ترکیب Mocha به‌عنوان فریم‌ورک تست و Chai به‌عنوان کتابخانهٔ assertion یکی از رایج‌ترین و قوی‌ترین گزینه‌هاست. در این مقاله به صورت عملی و گام‌به‌گام طرز نصب، نوشتن تست‌های سینکِ و آسنکرون، سبک‌های assertion و نکات پیشرفته را بررسی می‌کنیم.

چرا Mocha و Chai؟

  • Mocha: ساختار describe/it، hooks (before/after)، و پشتیبانی از async/await و Promise.
  • Chai: خوانایی بالای assertions با سه سبک اصلی — expect، should و assert.
  • هماهنگی با ابزارهای دیگر مثل Sinon برای spies/mocks و nyc برای پوشش کد (coverage).

نصب و پیکربندی اولیه

npm init -y
npm install --save-dev mocha chai

این دستورات پروژهٔ npm را مقداردهی اولیه کرده و Mocha و Chai را به‌عنوان devDependencies نصب می‌کنند.

{
  "name": "my-project",
  "scripts": {
    "test": "mocha --recursive --reporter spec"
  },
  "devDependencies": {
    "mocha": "^10.0.0",
    "chai": "^4.0.0"
  }
}

در package.json اسکریپت test، mocha را فراخوانی می‌کنیم. گزینه –recursive برای جستجوی پوشهٔ test و –reporter spec خروجی خواناتری تولید می‌کند.

مثال عملی: یک ماژول ساده

// lib/calculator.js
function add(a, b) {
  return a + b;
}

function asyncDouble(n) {
  return new Promise((resolve) => setTimeout(() => resolve(n * 2), 50));
}

module.exports = { add, asyncDouble };

این فایل دو تابع ساده دارد: add به‌صورت همزمان و asyncDouble که یک Promise بازمی‌گرداند.

نوشتن تست سینک با Chai

// test/calculator.test.js
const { expect } = require('chai');
const { add } = require('../lib/calculator');

describe('Calculator - synchronous', function() {
  it('should add two numbers correctly', function() {
    expect(add(2, 3)).to.equal(5);
  });
});

در این تست از سبک expect استفاده کرده‌ایم. describe گروه‌بندی تست‌ها و it یک مورد تست را تعریف می‌کند. expect(value).to.equal(expected) چک می‌کند که مقدار برابر باشد.

سبک‌های مختلف Assertion در Chai

سبکنمونهتوضیح
expectexpect(x).to.equal(y)خوانا و محبوب؛ مناسب برای اغلب کاربردها
shouldx.should.equal(y)به صورت prototype تزریق می‌کند؛ نیاز به require(‘chai’).should()
assertassert.equal(x, y)سبک سنتی و تابعی

تست‌های آسنکرون — اشتباه رایج و اصلاح

یک اشتباه رایج عدم بازگردانی Promise یا فراموش کردن async/await است که باعث می‌شود Mocha تست را قبل از تکمیل عملیات آسنکرون پایان دهد.

// اشتباه: تست آسنکرون بدون return یا async/await
it('wrong async test', function() {
  asyncDouble(4).then(result => {
    expect(result).to.equal(8);
  });
});

در این مثال، چون Promise بازگردانده نشده، Mocha ممکن است قبل از اجرای callback then تست را موفق تلقی کند. اصلاح در ادامه آمده است:

// صحیح: بازگرداندن Promise
it('correct async test - return promise', function() {
  return asyncDouble(4).then(result => {
    expect(result).to.equal(8);
  });
});

// یا با async/await
it('correct async test - async/await', async function() {
  const result = await asyncDouble(4);
  expect(result).to.equal(8);
});

با بازگرداندن Promise یا استفاده از async/await، Mocha منتظر اجرای کامل عملیات آسنکرون می‌ماند و نتیجهٔ درست را گزارش می‌دهد.

استفاده از Hooks (before/after/ beforeEach/afterEach)

// test/hooks.test.js
const { expect } = require('chai');

let counter = 0;

describe('Hooks demo', function() {
  before(function() {
    // اجرا فقط یک بار قبل از تمام it ها
    counter = 1;
  });

  beforeEach(function() {
    // قبل از هر it اجرا می‌شود
    counter++;
  });

  afterEach(function() {
    // پاکسازی بعد از هر تست
    counter--;
  });

  after(function() {
    // اجرا فقط یک بار بعد از همه تست‌ها
    counter = 0;
  });

  it('uses hooks', function() {
    expect(counter).to.be.a('number');
  });
});

هوک‌ها برای مقداردهی اولیه، پاکسازی منابع و تنظیم محیط تست مفیدند. مراقب state مشترک بین تست‌ها باشید تا هر تست مستقل بماند.

نکات پیشرفته و بهترین شیوه‌ها

  • ایزوله کردن تست‌ها: از reset/state جدید برای هر تست استفاده کنید تا وابستگی بین تست‌ها حذف شود.
  • استفاده از ابزارهای mocking: برای mock کردن وابستگی‌ها از Sinon یا chai-spies بهره ببرید.
  • پوشش کد (coverage): با nyc می‌توانید میزان پوشش تست‌ها را بسنجید: npm install –save-dev nyc و اسکریپت “test-coverage”: “nyc npm test”.
  • CI: اجرای تست‌ها در هر commit با GitHub Actions یا GitLab CI تضمین‌کنندهٔ پایداری است.
  • خوانایی: تست‌ها باید به‌صورت توصیفی نوشته شوند—نام تست باید رفتار مورد انتظار را بیان کند.

نمونهٔ یکپارچه با coverage

// package.json (بخشی از فایل)
{
  "scripts": {
    "test": "mocha --recursive --reporter spec",
    "test:coverage": "nyc npm test"
  },
  "devDependencies": {
    "mocha": "^10.0.0",
    "chai": "^4.0.0",
    "nyc": "^15.0.0"
  }
}

استفاده از nyc باعث می‌شود گزارش دقیقی از خطوط تست‌شده علیه کل کد به‌دست آید که برای تصمیم‌گیری دربارهٔ نقاط بحرانی مفید است.

جمع‌بندی و نکات پایانی

  • Mocha ساختار و انعطاف‌پذیری لازم برای تست در Node.js را فراهم می‌کند.
  • Chai با سه سبک assertion خواناترین راه برای بیان انتظارهاست.
  • همیشه عملیات آسنکرون را یا return کنید یا از async/await استفاده کنید تا نتایج درست گزارش شوند.
  • از hooks برای مدیریت state و از ابزارهای mocking و coverage برای تست‌های قابل اعتمادتر بهره ببرید.

با رعایت این نکات می‌توانید مجموعهٔ تستی تمیز، قابل نگهداری و قابل اتکاء برای پروژه‌های Node.js بسازید که توسعهٔ سریع و امن را تسهیل می‌کند.

آیا این مطلب برای شما مفید بود ؟

خیر
بله
موضوعات شما در انجمن: