nock لـ TypeScript: محاكاة HTTP في الاختبارات
1. ما هي مكتبة nock ولماذا نحتاجها؟
nock هي مكتبة قوية لـ Node.js، مصممة لاعتراض ومحاكاة طلبات HTTP. تتيح للمطورين محاكاة الردود من الخدمات الخارجية (REST API، الخدمات المصغرة التابعة لجهات خارجية، webhooks) دون الحاجة إلى تنفيذ طلبات شبكة حقيقية. هذا مهم بشكل خاص عند كتابة اختبارات الوحدة واختبارات التكامل، حيث تكون الاستقرارية وسرعة التنفيذ ذات أهمية حاسمة.
لماذا نحتاج هذا؟ تخيل أن تطبيقك يرسل طلبات إلى بوابة دفع، أو API للطقس، أو قاعدة بيانات عبر HTTP. في بيئة الاختبار، لا تريد الاعتماد على توفر هذه الخدمات، أو سرعة استجابتها، أو حدود الطلبات. تسمح لك nock "باستبدال" هذه الطلبات، وإرجاع بيانات محددة مسبقًا. هذا يجعل الاختبارات سريعة، قابلة للتوقع، ومعزولة عن العوامل الخارجية. بالإضافة إلى ذلك، يمكنك اختبار سيناريوهات الأخطاء (مهلات، رموز 500، ردود فارغة) التي يصعب إعادة إنتاجها في الظروف الحقيقية.
في سياق TypeScript، تتكامل nock بشكل ممتاز مع أطر الاختبار الشائعة (Jest، Mocha، Vitest) وتدعم التوصيف (typing)، مما يجعل العمل بها مريحًا وآمنًا. تعترض المكتبة الطلبات على مستوى Node.js (عبر وحدات http و https)، لذا فهي تعمل مع أي عميل HTTP: axios، node-fetch، got وحتى fetch الأصلي (مع بعض التحفظات).
2. التثبيت
لتثبيت nock في مشروع TypeScript، قم بتنفيذ أحد الأوامر التالية في الطرفية:
# عبر npm
npm install --save-dev nock @types/nock
# عبر yarn
yarn add --dev nock @types/nock
# عبر pnpm
pnpm add --save-dev nock @types/nock
ملاحظة مهمة: حزمة @types/nock تحتوي على تعريفات الأنواع لـ TypeScript. إذا كنت تستخدم TypeScript 4.0+، فقد تكون الأنواع مدمجة بالفعل في المكتبة نفسها، ولكن من الأفضل تثبيتها بشكل صريح لضمان التوافق.
3. البداية السريعة
لنكتب اختبارًا بسيطًا يوضح العمل الأساسي لـ nock. لنفترض أن لدينا دالة تجلب بيانات مستخدم من API خارجي.
// userService.ts
import axios from 'axios';
export async function getUser(userId: number): Promise<{ id: number; name: string; email: string }> {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users/${userId}`);
return response.data;
}
الآن لنكتب اختبارًا يحاكي هذا الطلب:
// userService.test.ts
import nock from 'nock';
import { getUser } from './userService';
describe('getUser', () => {
afterEach(() => {
nock.cleanAll(); // مسح جميع المحاكيات بعد كل اختبار
});
it('يجب أن يعيد بيانات المستخدم', async () => {
// 1. إعداد الاعتراض
const mockUser = { id: 1, name: 'John Doe', email: 'john@example.com' };
nock('https://jsonplaceholder.typicode.com')
.get('/users/1')
.reply(200, mockUser);
// 2. استدعاء الدالة المختبرة
const user = await getUser(1);
// 3. التحقق من النتيجة
expect(user).toEqual(mockUser);
});
it('يجب أن يرمي خطأ عند 500', async () => {
nock('https://jsonplaceholder.typicode.com')
.get('/users/1')
.reply(500, { error: 'Server Error' });
await expect(getUser(1)).rejects.toThrow();
});
});
يوضح هذا المثال كيف يمكن بسهولة استبدال رد الخادم واختبار كل من السيناريوهات الناجحة والخاطئة.
4. الأساسيات
...