راهنمای جامع تست یکپارچهسازی API با Supertest، شامل راهاندازی، بهترین شیوهها و تکنیکهای پیشرفته برای تست قوی برنامهها.
تست یکپارچهسازی: تسلط بر تست API با Supertest
در دنیای توسعه نرمافزار، اطمینان از عملکرد صحیح کامپوننتهای منفرد به صورت مجزا (تست واحد) امری حیاتی است. با این حال، به همان اندازه مهم است که تأیید کنیم این کامپوننتها به طور یکپارچه با یکدیگر کار میکنند. اینجاست که تست یکپارچهسازی وارد عمل میشود. تست یکپارچهسازی بر اعتبارسنجی تعامل بین ماژولها یا سرویسهای مختلف درون یک برنامه تمرکز دارد. این مقاله به طور عمیق به تست یکپارچهسازی، به ویژه با تمرکز بر تست API با Supertest، یک کتابخانه قدرتمند و کاربرپسند برای تست ادعاهای HTTP در Node.js، میپردازد.
تست یکپارچهسازی چیست؟
تست یکپارچهسازی نوعی از تست نرمافزار است که ماژولهای نرمافزاری منفرد را با هم ترکیب کرده و آنها را به عنوان یک گروه تست میکند. هدف آن آشکار ساختن نقصها در تعاملات بین واحدهای یکپارچه شده است. برخلاف تست واحد که بر کامپوننتهای فردی تمرکز دارد، تست یکپارچهسازی جریان داده و جریان کنترل بین ماژولها را تأیید میکند. رویکردهای رایج تست یکپارچهسازی عبارتند از:
- یکپارچهسازی بالا به پایین (Top-down): شروع با ماژولهای سطح بالاتر و یکپارچهسازی به سمت پایین.
- یکپارچهسازی پایین به بالا (Bottom-up): شروع با ماژولهای سطح پایینتر و یکپارچهسازی به سمت بالا.
- یکپارچهسازی انفجاری (Big-bang): یکپارچهسازی همزمان تمام ماژولها. این رویکرد به دلیل دشواری در جداسازی مشکلات، عموماً کمتر توصیه میشود.
- یکپارچهسازی ساندویچی (Sandwich): ترکیبی از یکپارچهسازی بالا به پایین و پایین به بالا.
در زمینه APIها، تست یکپارچهسازی شامل تأیید این است که APIهای مختلف به درستی با هم کار میکنند، دادههای منتقل شده بین آنها سازگار است و سیستم کلی طبق انتظار عمل میکند. به عنوان مثال، یک برنامه تجارت الکترونیک را با APIهای جداگانه برای مدیریت محصول، احراز هویت کاربر و پردازش پرداخت تصور کنید. تست یکپارچهسازی اطمینان حاصل میکند که این APIها به درستی با هم ارتباط برقرار میکنند و به کاربران اجازه میدهد محصولات را مرور کنند، به طور امن وارد شوند و خرید خود را تکمیل کنند.
چرا تست یکپارچهسازی API مهم است؟
تست یکپارچهسازی API به دلایل متعددی حیاتی است:
- تضمین قابلیت اطمینان سیستم: به شناسایی مشکلات یکپارچهسازی در اوایل چرخه توسعه کمک میکند و از خرابیهای غیرمنتظره در محیط پروداکشن جلوگیری میکند.
- اعتبارسنجی یکپارچگی دادهها: تأیید میکند که دادهها به درستی بین APIهای مختلف منتقل و تبدیل میشوند.
- بهبود عملکرد برنامه: میتواند گلوگاههای عملکردی مربوط به تعاملات API را آشکار کند.
- افزایش امنیت: میتواند آسیبپذیریهای امنیتی ناشی از یکپارچهسازی نامناسب API را شناسایی کند. به عنوان مثال، اطمینان از احراز هویت و مجوزدهی مناسب هنگام ارتباط APIها.
- کاهش هزینههای توسعه: رفع مشکلات یکپارچهسازی در مراحل اولیه به طور قابل توجهی ارزانتر از رسیدگی به آنها در مراحل بعدی چرخه توسعه است.
یک پلتفرم جهانی رزرو سفر را در نظر بگیرید. تست یکپارچهسازی API برای اطمینان از ارتباط روان بین APIهایی که رزرو پرواز، رزرو هتل و درگاههای پرداخت از کشورهای مختلف را مدیریت میکنند، حیاتی است. عدم یکپارچهسازی صحیح این APIها میتواند منجر به رزروهای نادرست، شکست در پرداخت و تجربه کاربری ضعیف شود و بر اعتبار و درآمد پلتفرم تأثیر منفی بگذارد.
معرفی Supertest: ابزاری قدرتمند برای تست API
Supertest یک انتزاع سطح بالا برای تست درخواستهای HTTP است. این کتابخانه یک API روان و راحت برای ارسال درخواست به برنامه شما و ادعا (assert) در مورد پاسخها فراهم میکند. Supertest که بر پایه Node.js ساخته شده، به طور خاص برای تست سرورهای HTTP در Node.js طراحی شده است. این کتابخانه به طرز استثنایی با فریمورکهای تست محبوب مانند Jest و Mocha کار میکند.
ویژگیهای کلیدی Supertest:
- سادگی در استفاده: Supertest یک API ساده و شهودی برای ارسال درخواستهای HTTP و انجام ادعاها ارائه میدهد.
- تست ناهمگام: به طور یکپارچه عملیات ناهمگام را مدیریت میکند، که آن را برای تست APIهایی که به منطق ناهمگام متکی هستند ایدهآل میسازد.
- رابط روان (Fluent Interface): یک رابط روان ارائه میدهد که به شما امکان میدهد متدها را برای تستهای مختصر و خوانا به هم زنجیر کنید.
- پشتیبانی جامع از ادعاها: از طیف گستردهای از ادعاها برای تأیید کدهای وضعیت، هدرها و بدنه پاسخ پشتیبانی میکند.
- یکپارچهسازی با فریمورکهای تست: به طور یکپارچه با فریمورکهای تست محبوب مانند Jest و Mocha ادغام میشود و به شما امکان میدهد از زیرساخت تست موجود خود استفاده کنید.
راهاندازی محیط تست شما
قبل از شروع، بیایید یک محیط تست پایه را راهاندازی کنیم. ما فرض میکنیم که شما Node.js و npm (یا yarn) را نصب کردهاید. ما از Jest به عنوان فریمورک تست و از Supertest برای تست API استفاده خواهیم کرد.
- ایجاد یک پروژه Node.js:
mkdir api-testing-example
cd api-testing-example
npm init -y
- نصب وابستگیها:
npm install --save-dev jest supertest
npm install express # یا فریمورک دلخواه شما برای ساخت API
- پیکربندی Jest: قطعه کد زیر را به فایل
package.json
خود اضافه کنید:
{
"scripts": {
"test": "jest"
}
}
- ایجاد یک اندپوینت ساده API: یک فایل با نام
app.js
(یا مشابه) با کد زیر ایجاد کنید:
const express = require('express');
const app = express();
const port = 3000;
app.get('/hello', (req, res) => {
res.send('Hello, World!');
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
module.exports = app; // اکسپورت برای تست
نوشتن اولین تست Supertest شما
حالا که محیط خود را راهاندازی کردهایم، بیایید یک تست ساده Supertest برای تأیید اندپوینت API خود بنویسیم. یک فایل با نام app.test.js
(یا مشابه) در ریشه پروژه خود ایجاد کنید:
const request = require('supertest');
const app = require('./app');
describe('GET /hello', () => {
it('responds with 200 OK and returns "Hello, World!"', async () => {
const response = await request(app).get('/hello');
expect(response.statusCode).toBe(200);
expect(response.text).toBe('Hello, World!');
});
});
توضیح:
- ما
supertest
و برنامه Express خود را وارد میکنیم. - ما از
describe
برای گروهبندی تستهای خود استفاده میکنیم. - ما از
it
برای تعریف یک مورد تست خاص استفاده میکنیم. - ما از
request(app)
برای ایجاد یک ایجنت Supertest استفاده میکنیم که به برنامه ما درخواست ارسال میکند. - ما از
.get('/hello')
برای ارسال یک درخواست GET به اندپوینت/hello
استفاده میکنیم. - ما از
await
برای منتظر ماندن برای پاسخ استفاده میکنیم. متدهای Supertest پرامیس برمیگردانند، که به ما اجازه میدهد از async/await برای کد تمیزتر استفاده کنیم. - ما از
expect(response.statusCode).toBe(200)
برای ادعای این که کد وضعیت پاسخ 200 OK است، استفاده میکنیم. - ما از
expect(response.text).toBe('Hello, World!')
برای ادعای این که بدنه پاسخ "Hello, World!" است، استفاده میکنیم.
برای اجرای تست، دستور زیر را در ترمینال خود اجرا کنید:
npm test
اگر همه چیز به درستی تنظیم شده باشد، باید ببینید که تست با موفقیت پاس میشود.
تکنیکهای پیشرفته Supertest
Supertest طیف گستردهای از ویژگیها را برای تست پیشرفته API ارائه میدهد. بیایید برخی از آنها را بررسی کنیم.
۱. ارسال بدنه درخواست (Request Body)
برای ارسال داده در بدنه درخواست، میتوانید از متد .send()
استفاده کنید. به عنوان مثال، بیایید یک اندپوینت ایجاد کنیم که دادههای JSON را میپذیرد:
app.post('/users', express.json(), (req, res) => {
const { name, email } = req.body;
// شبیهسازی ایجاد کاربر در پایگاه داده
const user = { id: Date.now(), name, email };
res.status(201).json(user);
});
در اینجا نحوه تست این اندپوینت با استفاده از Supertest آمده است:
describe('POST /users', () => {
it('creates a new user', async () => {
const userData = {
name: 'John Doe',
email: 'john.doe@example.com',
};
const response = await request(app)
.post('/users')
.send(userData)
.expect(201);
expect(response.body).toHaveProperty('id');
expect(response.body.name).toBe(userData.name);
expect(response.body.email).toBe(userData.email);
});
});
توضیح:
- ما از
.post('/users')
برای ارسال یک درخواست POST به اندپوینت/users
استفاده میکنیم. - ما از
.send(userData)
برای ارسال شیءuserData
در بدنه درخواست استفاده میکنیم. Supertest به طور خودکار هدرContent-Type
را بهapplication/json
تنظیم میکند. - ما از
.expect(201)
برای ادعای این که کد وضعیت پاسخ 201 Created است، استفاده میکنیم. - ما از
expect(response.body).toHaveProperty('id')
برای ادعای این که بدنه پاسخ دارای یک خاصیتid
است، استفاده میکنیم. - ما از
expect(response.body.name).toBe(userData.name)
وexpect(response.body.email).toBe(userData.email)
برای ادعای این که خصوصیاتname
وemail
در بدنه پاسخ با دادههایی که در درخواست فرستادیم مطابقت دارد، استفاده میکنیم.
۲. تنظیم هدرها
برای تنظیم هدرهای سفارشی در درخواستهای خود، میتوانید از متد .set()
استفاده کنید. این برای تنظیم توکنهای احراز هویت، انواع محتوا یا سایر هدرهای سفارشی مفید است.
describe('GET /protected', () => {
it('requires authentication', async () => {
const response = await request(app).get('/protected').expect(401);
});
it('returns 200 OK with a valid token', async () => {
// شبیهسازی دریافت یک توکن معتبر
const token = 'valid-token';
const response = await request(app)
.get('/protected')
.set('Authorization', `Bearer ${token}`)
.expect(200);
expect(response.text).toBe('Protected Resource');
});
});
توضیح:
- ما از
.set('Authorization', `Bearer ${token}`)
برای تنظیم هدرAuthorization
بهBearer ${token}
استفاده میکنیم.
۳. مدیریت کوکیها
Supertest همچنین میتواند کوکیها را مدیریت کند. شما میتوانید کوکیها را با استفاده از متد .set('Cookie', ...)
تنظیم کنید، یا میتوانید از خاصیت .cookies
برای دسترسی و تغییر کوکیها استفاده کنید.
۴. تست آپلود فایل
Supertest میتواند برای تست اندپوینتهای API که آپلود فایل را مدیریت میکنند، استفاده شود. شما میتوانید از متد .attach()
برای پیوست کردن فایلها به درخواست استفاده کنید.
۵. استفاده از کتابخانههای ادعا (Chai)
در حالی که کتابخانه ادعای داخلی Jest برای بسیاری از موارد کافی است، شما همچنین میتوانید از کتابخانههای ادعای قدرتمندتری مانند Chai با Supertest استفاده کنید. Chai یک سینتکس ادعای گویاتر و انعطافپذیرتر ارائه میدهد. برای استفاده از Chai، باید آن را نصب کنید:
npm install --save-dev chai
سپس، میتوانید Chai را به فایل تست خود وارد کرده و از ادعاهای آن استفاده کنید:
const request = require('supertest');
const app = require('./app');
const chai = require('chai');
const expect = chai.expect;
describe('GET /hello', () => {
it('responds with 200 OK and returns "Hello, World!"', async () => {
const response = await request(app).get('/hello');
expect(response.statusCode).to.equal(200);
expect(response.text).to.equal('Hello, World!');
});
});
نکته: ممکن است لازم باشد Jest را برای کار صحیح با Chai پیکربندی کنید. این کار اغلب شامل افزودن یک فایل راهاندازی است که Chai را وارد کرده و آن را برای کار با expect
سراسری Jest پیکربندی میکند.
۶. استفاده مجدد از ایجنتها
برای تستهایی که نیاز به راهاندازی یک محیط خاص دارند (مثلاً احراز هویت)، اغلب استفاده مجدد از یک ایجنت Supertest مفید است. این کار از کد راهاندازی تکراری در هر مورد تست جلوگیری میکند.
describe('Authenticated API Tests', () => {
let agent;
beforeAll(() => {
agent = request.agent(app); // ایجاد یک ایجنت پایدار
// شبیهسازی احراز هویت
return agent
.post('/login')
.send({ username: 'testuser', password: 'password123' });
});
it('can access a protected resource', async () => {
const response = await agent.get('/protected').expect(200);
expect(response.text).toBe('Protected Resource');
});
it('can perform other actions that require authentication', async () => {
// اقدامات احراز هویت شده دیگر را در اینجا انجام دهید
});
});
در این مثال، ما یک ایجنت Supertest را در هوک beforeAll
ایجاد کرده و آن را احراز هویت میکنیم. سپس تستهای بعدی در بلوک describe
میتوانند از این ایجنت احراز هویت شده مجدداً استفاده کنند بدون اینکه برای هر تست دوباره احراز هویت کنند.
بهترین شیوهها برای تست یکپارچهسازی API با Supertest
برای اطمینان از تست یکپارچهسازی API مؤثر، بهترین شیوههای زیر را در نظر بگیرید:
- تست گردشکارهای سرتاسری: بر روی تست گردشکارهای کامل کاربر به جای اندپوینتهای API مجزا تمرکز کنید. این به شناسایی مشکلات یکپارچهسازی کمک میکند که ممکن است هنگام تست APIهای فردی به صورت مجزا آشکار نباشند.
- استفاده از دادههای واقعی: از دادههای واقعی در تستهای خود برای شبیهسازی سناریوهای دنیای واقعی استفاده کنید. این شامل استفاده از فرمتهای داده معتبر، مقادیر مرزی و دادههای بالقوه نامعتبر برای تست مدیریت خطا است.
- جداسازی تستهای خود: اطمینان حاصل کنید که تستهای شما از یکدیگر مستقل هستند و به وضعیت مشترک تکیه نمیکنند. این باعث میشود تستهای شما قابل اعتمادتر و اشکالزدایی آنها آسانتر شود. استفاده از یک پایگاه داده تست اختصاصی یا ماک کردن وابستگیهای خارجی را در نظر بگیرید.
- ماک کردن وابستگیهای خارجی: از ماکینگ برای جداسازی API خود از وابستگیهای خارجی مانند پایگاههای داده، APIهای شخص ثالث یا سایر سرویسها استفاده کنید. این کار تستهای شما را سریعتر و قابل اعتمادتر میکند و همچنین به شما امکان میدهد سناریوهای مختلف را بدون اتکا به در دسترس بودن سرویسهای خارجی تست کنید. کتابخانههایی مانند
nock
برای ماک کردن درخواستهای HTTP مفید هستند. - نوشتن تستهای جامع: برای پوشش تست جامع، از جمله تستهای مثبت (تأیید پاسخهای موفق)، تستهای منفی (تأیید مدیریت خطا) و تستهای مرزی (تأیید موارد حاشیهای) تلاش کنید.
- خودکارسازی تستهای خود: تستهای یکپارچهسازی API خود را در خط لوله یکپارچهسازی مداوم (CI) خود ادغام کنید تا اطمینان حاصل شود که هر زمان تغییراتی در کدبیس ایجاد میشود، به طور خودکار اجرا میشوند. این به شناسایی زودهنگام مشکلات یکپارچهسازی و جلوگیری از رسیدن آنها به پروداکشن کمک میکند.
- مستندسازی تستهای خود: تستهای یکپارچهسازی API خود را به وضوح و به طور خلاصه مستند کنید. این کار درک هدف تستها و نگهداری آنها را در طول زمان برای سایر توسعهدهندگان آسانتر میکند.
- استفاده از متغیرهای محیطی: اطلاعات حساس مانند کلیدهای API، رمزهای عبور پایگاه داده و سایر مقادیر پیکربندی را به جای هاردکد کردن در تستهای خود، در متغیرهای محیطی ذخیره کنید. این کار تستهای شما را امنتر و پیکربندی آنها را برای محیطهای مختلف آسانتر میکند.
- در نظر گرفتن قراردادهای API: از تست قرارداد API برای تأیید اینکه API شما از یک قرارداد تعریف شده (مانند OpenAPI/Swagger) پیروی میکند، استفاده کنید. این به اطمینان از سازگاری بین سرویسهای مختلف کمک میکند و از تغییرات مخرب جلوگیری میکند. ابزارهایی مانند Pact میتوانند برای تست قرارداد استفاده شوند.
اشتباهات رایج که باید از آنها اجتناب کرد
- عدم جداسازی تستها: تستها باید مستقل باشند. از اتکا به نتیجه تستهای دیگر خودداری کنید.
- تست جزئیات پیادهسازی: بر رفتار و قرارداد API تمرکز کنید، نه بر پیادهسازی داخلی آن.
- نادیده گرفتن مدیریت خطا: نحوه مدیریت ورودیهای نامعتبر، موارد حاشیهای و خطاهای غیرمنتظره توسط API خود را به طور کامل تست کنید.
- صرفنظر کردن از تست احراز هویت و مجوزدهی: اطمینان حاصل کنید که مکانیزمهای امنیتی API شما به درستی تست شدهاند تا از دسترسی غیرمجاز جلوگیری شود.
نتیجهگیری
تست یکپارچهسازی API بخش اساسی فرآیند توسعه نرمافزار است. با استفاده از Supertest، میتوانید به راحتی تستهای یکپارچهسازی API جامع و قابل اعتمادی بنویسید که به تضمین کیفیت و پایداری برنامه شما کمک میکند. به یاد داشته باشید که بر روی تست گردشکارهای سرتاسری، استفاده از دادههای واقعی، جداسازی تستها و خودکارسازی فرآیند تست خود تمرکز کنید. با پیروی از این بهترین شیوهها، میتوانید به طور قابل توجهی خطر مشکلات یکپارچهسازی را کاهش دهید و محصولی قویتر و قابل اعتمادتر ارائه دهید.
همانطور که APIها به پیش راندن برنامههای مدرن و معماریهای میکروسرویس ادامه میدهند، اهمیت تست قوی API، و به ویژه تست یکپارچهسازی، فقط بیشتر خواهد شد. Supertest مجموعه ابزار قدرتمند و در دسترسی را برای توسعهدهندگان در سراسر جهان فراهم میکند تا از قابلیت اطمینان و کیفیت تعاملات API خود اطمینان حاصل کنند.