
در مقاله قبلی، با مبانی تست واحد و TDD آشنا شدیم. در این مقاله تکنیکهای پیشرفته تست نویسی واحد رو با هم بررسی میکنیم. در این مقاله، ابزارهای قدرتمندی رو یاد میگیریم که به ما کمک میکنن تستهای حرفهایتر و انعطافپذیرتری بنویسیم.
توی جاوااسکریپت (و قطعا تایپاسکریپت)، تفاوت بین toBe و toEqual بسیار مهمه و انتخاب نادرست، میتونه به تستهای اشتباه و گمراهکننده منجر بشه. داستان به این حقیقت برمیگرده که دو متغیر از نوع reference در جاوااسکریپت، اگر رفرنس مشترکی نداشته باشند، حتی اگرمقادیر کاملا یکسانی داشته باشند، باز هم با هم مساوی نیستند. مثال:
let a = { x:1 }; let b = { x:1 }; console.log(a===b); // false
متد toBe برای مقایسه مرجعی استفاده میشه و مشابه === یا ()Object.is عمل میکنه:
test('strings should be strictly equal', () => { expect('string').toBe('string'); }); test('numbers should be strictly equal', () => { expect(2).toBe(2); }); test('booleans should be strictly equal', () => { expect(true).toBe(true); expect(false).toBe(false); }); test('undefined and null should be strictly equal to themselves', () => { expect(undefined).toBe(undefined); expect(null).toBe(null); });
همه تستهای فوق پاس میشن، اما وقتی به سراغ اشیا و آرایهها میریم، مشکلاتی پیش میاد:
test.fails('objects should not be strictly equal', () => { expect({ a: 1 }).toBe({ a: 1 }); // it fails! }); test.fails('arrays should not be strictly equal', () => { expect([1, 2, 3]).toBe([1, 2, 3]); // it fails! }); test.fails('functions should not be strictly equal', () => { expect(() => {}).toBe(() => {}); // it fails! });
پس میبینیم که استفاده از toBe در چنین مواردی راهکار درستی نیست و باید سراغ toEqual رفت.
متد toEqual برای مقایسه ساختاری استفاده میشه و به صورت shallow مقادیر رو بررسی میکنه:
test('objects with the same properties are equal', () => { expect({ a: 1, b: 2 }).toEqual({ a: 1, b: 2 }); }); test('arrays should be equal', () => { expect([1, 2, 3]).toEqual([1, 2, 3]); }); test('nested objects should be equal', () => { expect({ a: 1, b: { c: 2 } }).toEqual({ a: 1, b: { c: 2 } }); }); test('multi-dimensional arrays should be equal', () => { expect([1, [2, 3]]).toEqual([1, [2, 3]]); });
حالا تمامی تست های فوق به درستی پاس می شوند.
متد toStrictEqual نسخه سفتوسختتری از toEqual هست:
class Person { constructor(name) { this.name = name; } } test('objects with undefined properties are equal to objects without those properties', () => { expect({ a: 1 }).toEqual({ a: 1, b: undefined }); // ✅ موفق میشه }); test('objects with undefined properties are NOT strictly equal to objects without those properties', () => { expect({ a: 1 }).not.toStrictEqual({ a: 1, b: undefined }); // ✅ موفق میشه }); test('instances are equal to object literals with the same properties', () => { expect(new Person('Alice')).toEqual({ name: 'Alice' }); // ✅ موفق میشه }); test('instances are NOT strictly equal to object literals with the same properties', () => { expect(new Person('Alice')).not.toStrictEqual({ name: 'Alice' }); // ✅ موفق میشه });
دقت کنید که در تستهای فوق با not عدم تساوی رو بررسی کردیم.
متد toBe: وقتی میخوایم مطمئن بشیم دو متغیر به یک شیء اشاره میکنن
متد toEqual: وقتی میخوایم مطمئن بشیم دو شیء محتوای یکسانی دارن (معمولاً اینو میخوایم)
متد toStrictEqual: وقتی میخوایم نسبت به toEqual دقیقتر باشیم و undefinedها و انواع شیء رو هم بررسی کنیم
اگر بخواهیم یک مثال از مفهوم تساوی اینبار در ساختار کلاسی بزنیم، بدین صورت میشه.
class Calculator { constructor() { this.result = 0; this.history = []; } add(num) { this.result += num; this.history.push(`+${num}`); return this; } subtract(num) { this.result -= num; this.history.push(`-${num}`); return this; } }
حالا تستش:
test('calculator should work correctly', () => { const calc = new Calculator(); calc.add(5).subtract(2); expect(calc).not.toBe(new Calculator()); // it fails! expect(calc).toEqual({ result: 3, history: ['+5', '-2'] }); // it success! });
همسانیابی نامتقارن بدون شک یکی از قدرتمندترین ابزارهای تست نویسیه. matcher ها به ما اجازه میدن فقط قسمتهایی از دادهها رو تست کنیم که واقعاً مهم هستن و بقیه رو نادیده بگیریم. این کار باعث میشه تستهامون انعطافپذیرتر و مقاومتر نسبت به تغییرات غیرضروری بشن.
تصور کنید یه API داریم که اطلاعات کاربر رو برمیگردونه:
{ id: "user-123", name: "John Doe", email: "john@example.com", createdAt: "2024-01-15T10:30:00Z", lastLogin: "2024-01-20T15:45:00Z", settings: { theme: "dark", notifications: true } }
اگه بخوایم این رو با toEqual تست کنیم، باید همه فیلدها رو دقیق مشخص کنیم. این کار تست رو خیلی سفت میکنه و با هر تغییر کوچیک (مثلاً تغییر timestamp)، تست fail میشه.
همسانیابی نامتقارن به ما اجازه میدن فقط روی چیزایی تمرکز کنیم که واقعاً برامون مهم هستن.
مشاهده مقاله کامل در این آدرس:
https://farshidev.ir/Post/%D8%AA%D8%B3%D8%AA%E2%80%8C%D9%86%D9%88%DB%8C%D8%B3%DB%8C-javascript-typescript-unit-test-advanced
موفق باشید.