استفاده از Mocha و Chai در 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
| سبک | نمونه | توضیح |
|---|---|---|
| expect | expect(x).to.equal(y) | خوانا و محبوب؛ مناسب برای اغلب کاربردها |
| should | x.should.equal(y) | به صورت prototype تزریق میکند؛ نیاز به require(‘chai’).should() |
| assert | assert.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 بسازید که توسعهٔ سریع و امن را تسهیل میکند.
آیا این مطلب برای شما مفید بود ؟




