اختبار كود JavaScript مع Jest للدمى. الجزء الأول

مرحبا يا هابر! هذا الدليل هو الجزء الأول في سلسلة مخططة من المقالات حول إطار اختبار رائع مثل Jest. ستكون المادة مفيدة للمبتدئين وأولئك الذين يتعرفون على الاختبارات للتو ، ويرغبون في دراسة هذا الإطار. سنناقش في الجزء الأول: كيفية البدء في الدعابة ، وكيفية كتابة اختبار بسيط ، وما هي الطرق المتاحة لمقارنة القيم التي يجب التحقق منها بالقيم المتوقعة. من يهتم - مرحبا بك في كات!

ما هي الدعابة؟


كما هو موضح في الصفحة الرئيسية للمشروع:
Jest هي بيئة اختبار JavaScript مذهلة مع التركيز على البساطة.
وبالفعل ، Jest بسيط للغاية. لا يتطلب إعدادات إضافية ، وسهل الفهم والاستخدام ، كما أن لديه وثائق جيدة جدًا. رائعة للمشاريع التي تستخدم Node و React و Angular و Vue و Babel و TypeScript والمزيد.
كما أنه مفتوح المصدر ومدعوم من Facebook.

التركيب


لتثبيت Jest في مشروعك ، قم بما يلي:

npm install --save-dev jest

إذا كنت تستخدم الغزل:

yarn add --dev jest

بعد التثبيت ، يمكنك تحديث قسم البرامج النصية في الحزمة الخاصة بك. json:

“scripts” : {
     “test”: “jest”
}

من خلال هذه المكالمة البسيطة ، يمكننا بالفعل تشغيل اختباراتنا (في الواقع ، سوف تتطلب الدعابة وجود اختبار واحد على الأقل).

يمكنك أيضًا التثبيت على مستوى العالم (ولكن لا أوصي بذلك ، لأن التثبيت العالمي للوحدات يمثل ممارسة سيئة بالنسبة لي):

npm install jest --global

وبناءً على ذلك ، بالنسبة للغزل:

yarn global add jest

بعد ذلك ، يمكنك استخدام الدعابة مباشرة من سطر الأوامر.

من خلال استدعاء الأمر jest --init في جذر المشروع ، بعد الإجابة عن بعض الأسئلة ، ستحصل على ملف إعدادات jest.config.js. أو يمكنك إضافة التكوين مباشرة إلى package.json الخاص بك. للقيام بذلك ، أضف مفتاح "jest" إلى جذر json وفي الكائن المقابل يمكنك إضافة الإعدادات التي تحتاجها. سنقوم بتحليل الخيارات نفسها في وقت لاحق. هذا ليس ضروريًا في هذه المرحلة ، حيث يمكن استخدام الدعابة على الفور ، بدون تكوينات إضافية.

الاختبار الأول


لنقم بإنشاء ملف first.test.js ونكتب اختبارنا الأول:

//first.test.js
test('My first test', () => {
    expect(Math.max(1, 5, 10)).toBe(10);
});

وقم بتشغيل اختباراتنا باستخدام npm run test أو مباشرة باستخدام الأمر jest (إذا كان مثبتًا بشكل عام). بعد الإطلاق ، سنرى تقريرًا عن اجتياز الاختبارات.

 <b>PASS</b>  ./first.test.js
  ✓ My first test (1 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.618 s, estimated 1 s

لنكسر اختبارنا ونجري الدعابة مرة أخرى:

//first.test.js
test('My first test', () => {
    expect(Math.max(1, 5, 10)).toBe(5);
});

كما نرى ، الآن لا يجتاز اختبارنا الاختبار. يعرض Jest معلومات تفصيلية حول مكان حدوث المشكلة ، والنتيجة المتوقعة ، وما حصلنا عليه بدلاً من ذلك.

الآن دعونا نحلل رمز الاختبار نفسه. وظيفة الاختبارتستخدم لإنشاء اختبار جديد. يستغرق ثلاث حجج (في المثال استخدمنا مكالمة مع وسيطتين). الأول عبارة عن سطر باسم الاختبار ، وستظهر دعامة في التقرير. والثاني هو دالة تحتوي على منطق اختبارنا. يمكنك أيضًا استخدام الوسيطة الثالثة - المهلة. إنه اختياري ، وقيمته الافتراضية هي 5 ثوان. يتم ضبطها بالمللي ثانية. هذه المعلمة ضرورية عندما نعمل مع رمز غير متزامن ونعيد وعدًا من وظيفة الاختبار. يشير إلى المدة التي يجب أن تنتظرها الدعابة حتى يتم حل الوعد. بعد هذا الوقت ، إذا لم يكن الوعد مسموحًا به - سيعتبر الدعابة أن الاختبار فشل. مزيد من المعلومات حول العمل مع المكالمات غير المتزامنة ستكون في الأجزاء التالية. بدلاً من ذلك ، يمكنك استخدامه () بدلاً من test ().. لا يوجد فرق بين هذه المكالمات. it () مجرد اسم مستعار على دالة test () .

داخل دالة الاختبار ، نسمي أولاً توقع () . ننقل إليه القيمة التي نريد التحقق منها. في حالتنا ، هذه نتيجة استدعاء Math.max (1 ، 5 ، 10) . يتوقع () إرجاع كائن مجمّع يحتوي على عدد من الأساليب لمقارنة القيمة الناتجة بالقيمة المتوقعة. إحدى الطرق التي استخدمناها هي toBe .

لنلق نظرة على أهم هذه الطرق:

  • toBe() — , , . Object.is(). === 0 -0, NaN c NaN.
  • toEqual() — , . . . .

    test('toEqual with objects', () => {
        expect({ foo: 'foo', subObject: { baz: 'baz' } })
            .toEqual({ foo: 'foo', subObject: { baz: 'baz' } });  // 
        expect({ foo: 'foo', subObject: { num: 0 } })
            .toEqual({ foo: 'foo', subObject: { baz: 'baz' } });  //    .
    });
    
    test('toEqual with arrays', () => {
        expect([11, 19, 5]).toEqual([11, 19, 5]); // 
        expect([11, 19, 5]).toEqual([11, 19]); // 
    });
    

  • toContain() — . ===.

    const arr = ['apple', 'orange', 'banana'];
    expect(arr).toContain('banana');
    expect(new Set(arr)).toContain('banana');
    expect('apple, orange, banana').toContain('banana');
    

  • toContainEqual() — .

    expect([{a: 1}, {b: 2}]).toContainEqual({a: 1});
    

  • toHaveLength() — length .

    expect([1, 2, 3, 4]).toHaveLength(4);
    expect('foo').toHaveLength(3);
    expect({ length: 1 }).toHaveLength(1);
    

  • toBeNull() — null.
  • toBeUndefined() — undefined.
  • toBeDefined()toBeUndefined. !== undefined.
  • toBeTruthy() — true. false, null, undefined, 0, NaN .
  • toBeFalsy()toBeTruthy(). false.
  • toBeGreaterThan() toBeGreaterThanOrEqual() — , >, >=.
  • toBeLessThan() toBeLessThanOrEqual()toBeGreaterThan() toBeGreaterThanOrEqual()
  • toBeCloseTo() — , , . .

    const num = 0.1 + 0.2; // 0.30000000000000004
    expect(num).toBeCloseTo(0.3);
    expect(Math.PI).toBeCloseTo(3.14, 2);
    

  • toMatch() — .

    expect('Banana').toMatch(/Ba/);
    

  • toThrow () - يستخدم عند الضرورة للتحقق من الاستثناء. يمكنك التحقق من كل من حقيقة الخطأ نفسه والتحقق من إلقاء استثناءات لفئة معينة ، إما عن طريق رسالة الخطأ أو عن طريق مراسلات الرسالة للتعبير العادي.

    function funcWithError() {
        throw new Error('some error');
    }   
    expect(funcWithError).toThrow();
    expect(funcWithError).toThrow(Error);
    expect(funcWithError).toThrow('some error');
    expect(funcWithError).toThrow(/some/);
    

  • لا - هذه الخاصية تسمح لك بالتحقق من عدم المساواة. يوفر كائنًا يحتوي على جميع الطرق المذكورة أعلاه ، ولكنه سيعمل بالعكس.

    expect(true).not.toBe(false);
    expect({ foo: 'bar' }).not.toEqual({});
    
    function funcWithoutError() {}
    expect(funcWithoutError).not.toThrow();
    

لنكتب بضعة اختبارات بسيطة. أولاً ، أنشئ وحدة بسيطة تحتوي على عدة طرق للعمل مع الدوائر.

// src/circle.js
const area = (radius) => Math.PI * radius ** 2;
const circumference = (radius) => 2 * Math.PI * radius;
module.exports = { area, circumference };

بعد ذلك ، أضف الاختبارات:

// tests/circle.test.js
const circle = require('../src/circle');

test('Circle area', () => {
    expect(circle.area(5)).toBeCloseTo(78.54);
    expect(circle.area()).toBeNaN();
});

test('Circumference', () => {
    expect(circle.circumference(11)).toBeCloseTo(69.1, 1);
    expect(circle.circumference()).toBeNaN();
});

في هذه التجارب، ونحن إيداعه نتيجة لأساليب 2 - منطقة و محيط . باستخدام طريقة toBeCloseTo ، تحققنا من النتيجة المتوقعة. في الحالة الأولى ، قمنا بفحص أو أن المساحة المحسوبة للدائرة التي يبلغ نصف قطرها 5 تساوي تقريبًا 78.54 ، في حين أن الفرق مع القيمة التي تم الحصول عليها (ستكون 78.53981633974483) ليس كبيرًا وسيتم حساب الاختبار. في الثانية ، أشرنا إلى أننا مهتمون بالتحقق من خلال علامة عشرية واحدة. اتصلنا أيضًا بأساليبنا بدون أي وسيطات وتحققنا من النتيجة باستخدام toBeNaN . نظرًا لأن نتيجة إعدامهم ستكون NaN ، فسيتم اجتياز الاختبارات بنجاح.

لنأخذ مثالاً آخر. لنقم بإنشاء وظيفة تقوم بتصفية مجموعة من المنتجات حسب السعر:

// src/productFilter.js
const byPriceRange = (products, min, max) =>
         products.filter(item => item.price >= min && item.price <= max);
module.exports = { byPriceRange };

وإضافة اختبار:

// tests/product.test.js
const productFilter = require('../src/producFilter');

const products = [
    { name: 'onion', price: 12 },
    { name: 'tomato', price: 26 },
    { name: 'banana', price: 29 },
    { name: 'orange', price: 38 }
];

test('Test product filter by range', () => {
    const FROM = 15;
    const TO = 30;
    const filteredProducts = productFilter.byPriceRange(products, FROM, TO);

    expect(filteredProducts).toHaveLength(2);
    expect(filteredProducts).toContainEqual({ name: 'tomato', price: 26 });
    expect(filteredProducts).toEqual([{ name: 'tomato', price: 26 }, { name: 'banana', price: 29 }]);
    expect(filteredProducts[0].price).toBeGreaterThanOrEqual(FROM);
    expect(filteredProducts[1].price).toBeLessThanOrEqual(TO);
    expect(filteredProducts).not.toContainEqual({ name: 'orange', price: 38 });
});

في هذا الاختبار ، سوف نتحقق من نتيجة وظيفة byRangePrice . أولاً ، قمنا بفحص المراسلات الخاصة بطول المصفوفة الناتجة إلى المجموعة المتوقعة - 2. يتطلب الفحص التالي أن يحتوي العنصر على العنصر - {name: 'tomato'، price: 26}. الكائن في الصفيف والكائن الذي تم تمريره إلى ContainEqual كائنان مختلفان ، وليس مرجعًا إلى نفس الكائن . ولكن سيقارن toContainEqual كل خاصية. نظرًا لأن كلا الكائنين متطابقين ، فسيتم التحقق بنجاح. بعد ذلك ، نستخدم toEqual للتحقق من بنية الصفيف بأكمله وعناصره. و toBeGreaterThanOrEqual و أساليب toBeLessThanOrEqual ستساعدنا تحقق من الأسعار من العنصر الأول والثاني من المصفوفة. وأخيرا، داعيا not.toContainEqualسيتحقق مما إذا كان العنصر موجودًا في المصفوفة - {name: 'orange'، price: 38} ، والذي يجب ألا يكون موجودًا حسب الشرط.

في هذه الأمثلة ، كتبنا بعض الاختبارات البسيطة باستخدام وظائف التحقق الموضحة أعلاه. في الأجزاء التالية ، سنناقش كيفية العمل مع التعليمات البرمجية غير المتزامنة ، ووظائف الدعابة التي لم يتم تناولها في هذا الجزء من البرنامج التعليمي ، سنتحدث عن إعدادها ، وأكثر من ذلك بكثير.

All Articles