اختبار JavaScript مع Jest

Jest هي مكتبة لاختبار كود JavaScript. إنه مشروع مفتوح المصدر تتم صيانته بواسطة Facebook ، وهو مناسب بشكل خاص لاختبار كود React ، على الرغم من أنه لا يقتصر على ذلك: يمكنه اختبار أي كود JavaScript. Jest سريع جدا وسهل الاستخدام

مقدمة في Jest

Jest هي مكتبة لاختبار كود JavaScript.

إنه مشروع مفتوح المصدر تتم صيانته بواسطة Facebook ، وهو مناسب بشكل خاص لاختبار كود React ، على الرغم من أنه لا يقتصر على ذلك: يمكنه اختبار أي كود JavaScript. قوتها هي:

  • إنه سريع
  • يمكن أن تؤدياختبار لقطة
  • إنه برأي ، ويقدم كل شيء خارج الصندوق دون مطالبتك بالاختيار

Jest هي أداة تشبه إلى حد بعيد Mocha ، على الرغم من وجود اختلافات بينهما:

  • Mocha أقل رأيًا ، بينما Jest لديها مجموعة معينة من الاتفاقيات
  • يتطلب Mocha مزيدًا من التكوين ، بينما يعمل Jest عادةً خارج الصندوق ، وذلك بفضل الرأي
  • Mocha أقدم وأكثر رسوخًا ، مع المزيد من تكامل الأدوات

في رأيي ، فإن أكبر ميزة لـ Jest هي أنه حل خارج الصندوق يعمل دون الحاجة إلى التفاعل مع مكتبات اختبار أخرى لأداء وظيفته.

تثبيت

يتم تثبيت Jest تلقائيًا فيcreate-react-app، لذلك إذا كنت تستخدم ذلك ، فلن تحتاج إلى تثبيت Jest.

يمكن تثبيت Jest في أي مشروع آخر باستخدامغزل:

yarn add --dev jest

أوnpm:

npm install --save-dev jest

لاحظ كيف نوجه كلاهما لوضع Jest فيdevDependenciesجزء منpackage.jsonالملف ، بحيث يتم تثبيته فقط في بيئة التطوير وليس في الإنتاج.

أضف هذا السطر إلى جزء البرامج النصية من ملفpackage.jsonملف:

{
  "scripts": {
    "test": "jest"
  }
}

بحيث يمكن إجراء الاختبارات باستخدامyarn testأوnpm run test.

بدلاً من ذلك ، يمكنك تثبيت Jest عالميًا:

yarn global add jest

وقم بإجراء جميع اختباراتك باستخدام ملفjestأداة سطر الأوامر.

أنشئ اختبار Jest الأول

تم إنشاء المشاريع باستخدامcreate-react-appتثبيت Jest وتكوينه مسبقًا خارج الصندوق ، ولكن إضافة Jest إلى أي مشروع أمر سهل مثل الكتابة

yarn add --dev jest

أضف إلى ملفpackage.jsonهذا الخط:

{
  "scripts": {
    "test": "jest"
  }
}

وإجراء الاختبارات الخاصة بك عن طريق التنفيذyarn testفي قوقعتك.

الآن ، ليس لديك أي اختبارات هنا ، لذلك لن يتم تنفيذ أي شيء:

Testing with Yarn

لنقم بإنشاء الاختبار الأول. افتحmath.jsملف واكتب دالتين سنختبرهما لاحقًا:

const sum = (a, b) => a + b
const mul = (a, b) => a * b
const sub = (a, b) => a - b
const div = (a, b) => a / b

module.exports = { sum, mul, sub, div }

الآن قم بإنشاء ملفmath.test.jsفي نفس المجلد ، وهناك سنستخدم Jest لاختبار الوظائف المحددة فيmath.js:

const { sum, mul, sub, div } = require('./math')

test(‘Adding 1 + 1 equals 2’, () => { expect(sum(1, 1)).toBe(2) }) test(‘Multiplying 1 * 1 equals 1’, () => { expect(mul(1, 1)).toBe(1) }) test(‘Subtracting 1 - 1 equals 0’, () => { expect(sub(1, 1)).toBe(0) }) test(‘Dividing 1 / 1 equals 1’, () => { expect(div(1, 1)).toBe(1) })

ادارةyarn testيؤدي إلى تشغيل Jest على جميع ملفات الاختبار التي يعثر عليها ، وإرجاع النتيجة النهائية إلينا:

Passing tests

قم بتشغيل Jest باستخدام رمز VS

يعد Visual Studio Code محررًا رائعًا لتطوير JavaScript. التمديد الدعابةتقدم تكاملاً من الدرجة الأولى لاختباراتنا.

بمجرد تثبيته ، سيكتشف تلقائيًا ما إذا كنت قد قمت بتثبيت Jest في devDependency الخاص بك وتشغيل الاختبارات. يمكنك أيضًا استدعاء الاختبارات يدويًا عن طريق تحديد ملفدعابة: ابدأ عداءيأمر. سيتم تشغيل الاختبارات والبقاء في وضع المراقبة لإعادة تشغيلها كلما قمت بتغيير أحد الملفات التي تحتوي على اختبار (أو ملف اختبار):

A simple Jest test running in VS Code

المطابقون

في المقال السابق استخدمتهtoBe()كالوحيدالمطابق:

test('Adding 1 + 1 equals 2', () => {
  expect(sum(1, 1)).toBe(2)
})

المطابق هو طريقة تتيح لك اختبار القيم.

المطابقات الأكثر استخدامًا ، مقارنة قيمة نتيجةexpect()بالقيمة التي تم تمريرها كوسيطة ، هي:

  • toBeيقارن المساواة الصارمة ، باستخدام===
  • toEqualيقارن قيم متغيرين. إذا كان كائنًا أو مصفوفة ، فإنه يتحقق من تساوي جميع الخصائص أو العناصر
  • toBeNullيكون صحيحًا عند تمرير قيمة فارغة
  • toBeDefinedيكون صحيحًا عند تمرير قيمة محددة (عكس ما ورد أعلاه)
  • toBeUndefinedيكون صحيحًا عند تمرير قيمة غير محددة
  • toBeCloseToيستخدم لمقارنة القيم العائمة وتجنب أخطاء التقريب
  • toBeTruthyصواب إذا تم اعتبار القيمة صحيحة (مثلifيفعل)
  • toBeFalsyصواب إذا تم اعتبار القيمة خاطئة (مثلifيفعل)
  • toBeGreaterThanصحيح إذا كانت نتيجة توقع () أعلى من الوسيطة
  • toBeGreaterThanOrEqualصحيح إذا كانت نتيجة توقع () تساوي الوسيطة ، أو أعلى من الوسيطة
  • toBeLessThanصحيح إذا كانت نتيجة توقع () أقل من الوسيطة
  • toBeLessThanOrEqualصحيح إذا كانت نتيجة توقع () تساوي الوسيطة ، أو أقل من الوسيطة
  • toMatchيستخدم لمقارنة السلاسل معتعبير عادينمط مطابقة
  • toContainيستخدم في المصفوفات ، صحيح إذا كانت المصفوفة المتوقعة تحتوي على الوسيطة في مجموعة عناصرها
  • toHaveLength(number): يتحقق من طول المصفوفة
  • toHaveProperty(key, value): يتحقق مما إذا كان الكائن له خاصية ، ويتحقق اختياريًا من قيمته
  • toThrowيتحقق مما إذا كانت الوظيفة التي تمررها تؤدي إلى استثناء (بشكل عام) أو استثناء محدد
  • toBeInstanceOf(): يتحقق مما إذا كان الكائن هو مثيل لفئة

يمكن إبطال كل تلك المطابقات باستخدام.not.داخل البيان ، على سبيل المثال:

test('Adding 1 + 1 does not equal 3', () => {
  expect(sum(1, 1)).not.toBe(3)
})

للاستخدام مع الوعود ، يمكنك استخدامها.resolvesو.rejects:

expect(Promise.resolve('lemon')).resolves.toBe('lemon')

expect(Promise.reject(new Error(‘octopus’))).rejects.toThrow(‘octopus’)

يثبت

قبل إجراء اختباراتك ، ستحتاج إلى إجراء بعض التهيئة.

للقيام بشيء ما مرة واحدة قبل تشغيل جميع الاختبارات ، استخدم ملفbeforeAll()وظيفة:

beforeAll(() => {
  //do something
})

لأداء شيء ما قبل تشغيل كل اختبار ، استخدمbeforeEach():

beforeEach(() => {
  //do something
})

تمزيق

تمامًا كما تفعل مع الإعداد ، يمكنك أيضًا تنفيذ شيء ما بعد كل اختبار:

afterEach(() => {
  //do something
})

وبعد انتهاء جميع الاختبارات:

afterAll(() => {
  //do something
})

مجموعة الاختبارات باستخدام وصف ()

يمكنك إنشاء مجموعات من الاختبارات ، في ملف واحد ، تعزل وظيفتي الإعداد والتفكيك:

describe('first set', () => {
  beforeEach(() => {
    //do something
  })
  afterAll(() => {
    //do something
  })
  test(/*...*/)
  test(/*...*/)
})

describe(‘second set’, () => { beforeEach(() => { //do something }) beforeAll(() => { //do something }) test(//) test(//) })

اختبار الكود غير المتزامن

يمكن أن تحتوي الشفرة غير المتزامنة في JavaScript الحديثة على شكلين أساسيين: عمليات الاسترجاعات والوعود. علاوة على الوعود ، يمكننا استخدام عدم التزامن / انتظار.

عمليات الاسترجاعات

لا يمكنك إجراء اختبار في رد الاتصال ، لأن Jest لن ينفذه - ينتهي تنفيذ ملف الاختبار قبل استدعاء رد الاتصال. لإصلاح ذلك ، قم بتمرير معلمة إلى وظيفة الاختبار ، والتي يمكنك الاتصال بها بسهولةdone. ستنتظر Jest حتى تتصلdone()قبل إنهاء هذا الاختبار:

//uppercase.js
function uppercase(str, callback) {
  callback(str.toUpperCase())
}
module.exports = uppercase

//uppercase.test.js const uppercase = require(’./src/uppercase’)

test(uppercase 'test' to equal 'TEST', (done) => { uppercase(‘test’, (str) => { expect(str).toBe(‘TEST’) done() } })

Jest async test callback

وعود

مع وظائف تعود بالوعود ، نحنعودة الوعدمن الاختبار:

//uppercase.js
const uppercase = str => {
  return new Promise((resolve, reject) => {
    if (!str) {
      reject('Empty string')
      return
    }
    resolve(str.toUpperCase())
  })
}
module.exports = uppercase

//uppercase.test.js const uppercase = require(’./uppercase’) test(uppercase 'test' to equal 'TEST', () => { return uppercase(‘test’).then(str => { expect(str).toBe(‘TEST’) }) })

Jest async test promises

يمكن اختبار الوعود المرفوضة باستخدام.catch():

//uppercase.js
const uppercase = str => {
  return new Promise((resolve, reject) => {
    if (!str) {
      reject('Empty string')
      return
    }
    resolve(str.toUpperCase())
  })
}

module.exports = uppercase

//uppercase.test.js const uppercase = require(’./uppercase’)

test(uppercase 'test' to equal 'TEST', () => { return uppercase(’’).catch(e => { expect(e).toMatch(‘Empty string’) }) })

Jest async test catch

غير متزامن / انتظار

لاختبار الدوال التي تعيد الوعود ، يمكننا أيضًا استخدام غير متزامن / انتظار ، مما يجعل بناء الجملة واضحًا وبسيطًا للغاية:

//uppercase.test.js
const uppercase = require('./uppercase')
test(`uppercase 'test' to equal 'TEST'`, async () => {
  const str = await uppercase('test')
  expect(str).toBe('TEST')
})

Jest async test await async

السخرية

في الاختبار ،السخريةيسمح لك باختبار الوظائف التي تعتمد على:

  • قاعدة البيانات
  • شبكة الاتصالالطلبات
  • الولوج إلىالملفات
  • أيخارجيالنظام

لهذا السبب:

  1. تجري اختباراتكأسرع، مما يمنح وقتًا استجابة سريعًا أثناء التطوير
  2. اختباراتكمستقلحالة الشبكة ، أو حالة قاعدة البيانات
  3. اختباراتك لا تفعل ذلكتلوثأي تخزين للبيانات لأنها لا تلمس قاعدة البيانات
  4. لا يؤدي أي تغيير يتم إجراؤه في الاختبار إلى تغيير حالة الاختبارات اللاحقة ، ويجب أن تبدأ إعادة تشغيل مجموعة الاختبار من نقطة بداية معروفة وقابلة للتكرار
  5. لا داعي للقلق بشأن تقييد معدل مكالمات API وطلبات الشبكة

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

والأهم من ذلك ، إذا كنت تكتب أاختبار الوحدة، يجب أن تختبر وظيفة وظيفة ما بمعزل عن كل ما يمسها من أمتعة.

باستخدام mocks ، يمكنك التحقق مما إذا تم استدعاء وظيفة الوحدة النمطية والمعلمات التي تم استخدامها ، باستخدام:

  • expect().toHaveBeenCalled(): تحقق مما إذا تم استدعاء وظيفة التجسس
  • expect().toHaveBeenCalledTimes(): احسب عدد مرات استدعاء وظيفة التجسس
  • expect().toHaveBeenCalledWith(): تحقق مما إذا كان قد تم استدعاء الوظيفة بمجموعة محددة من المعلمات
  • expect().toHaveBeenLastCalledWith(): تحقق من معلمات آخر مرة تم فيها استدعاء الوظيفة

حزم التجسس دون التأثير على كود الوظائف

عند استيراد حزمة ، يمكنك إخبار Jest بـ "التجسس" على تنفيذ وظيفة معينة ، باستخدامspyOn()، دون التأثير على كيفية عمل هذه الطريقة.

مثال:

const mathjs = require('mathjs')

test(The mathjs log function, () => { const spy = jest.spyOn(mathjs, ‘log’) const result = mathjs.log(10000, 10)

expect(mathjs.log).toHaveBeenCalled() expect(mathjs.log).toHaveBeenCalledWith(10000, 10) })

اسخر من حزمة كاملة

يوفر Jest طريقة مناسبة للسخرية من حزمة كاملة. إنشاء__mocks__المجلد في جذر المشروع ، وفي هذا المجلد ، قم بإنشاء ملف JavaScript واحد لكل من الحزم الخاصة بك.

لنفترض أنك تستوردmathjs. إنشاء__mocks__/mathjs.jsملف في جذر مشروعك ، وأضف هذا المحتوى:

module.exports = {
  log: jest.fn(() => 'test')
}

هذا سوف يسخر من وظيفة السجل () للحزمة. أضف العديد من الوظائف كما تريد للسخرية منها:

const mathjs = require('mathjs')

test(The mathjs log function, () => { const result = mathjs.log(10000, 10) expect(result).toBe(‘test’) expect(mathjs.log).toHaveBeenCalled() expect(mathjs.log).toHaveBeenCalledWith(10000, 10) })

استهزأ بوظيفة واحدة

يمكنك السخرية من وظيفة واحدة باستخدامjest.fn():

const mathjs = require('mathjs')

mathjs.log = jest.fn(() => ‘test’) test(The mathjs log function, () => { const result = mathjs.log(10000, 10) expect(result).toBe(‘test’) expect(mathjs.log).toHaveBeenCalled() expect(mathjs.log).toHaveBeenCalledWith(10000, 10) })

تستطيع ايضا استخذامjest.fn().mockReturnValue('test')لإنشاء محاكاة بسيطة لا تفعل شيئًا سوى إرجاع قيمة.

نماذج مسبقة الصنع

يمكنك العثور على نماذج مسبقة الصنع للمكتبات الشعبية. على سبيل المثال هذه الحزمةhttps://github.com/jefflau/jest-fetch-mockيسمح لك بالسخريةfetch()المكالمات ، وتقديم عينات من قيم الإرجاع دون التفاعل مع الخادم الفعلي في اختباراتك.

اختبار اللقطة

يعد اختبار اللقطات ميزة رائعة جدًا تقدمها Jest. يمكنه حفظ كيفية عرض مكونات واجهة المستخدم الخاصة بك ومقارنتها بالاختبار الحالي ، مما يؤدي إلى حدوث خطأ إذا كان هناك عدم تطابق.

هذا اختبار بسيط على مكون التطبيق لملفcreate-react-appالتطبيق (تأكد من تثبيتreact-test-renderer):

import React from 'react'
import App from './App'
import renderer from 'react-test-renderer'

it(‘renders correctly’, () => { const tree = renderer.create(<App />).toJSON() expect(tree).toMatchSnapshot() })

في المرة الأولى التي تجري فيها هذا الاختبار ، يحفظ Jest اللقطة في ملف__snapshots__مجلد. إليك ما يحتويه App.test.js.snap:

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders correctly 1`] = `
<div
  className="App"
>
  <header
    className="App-header"
  >
    <img
      alt="logo"
      className="App-logo"
      src="logo.svg"
    />
    <h1
      className="App-title"
    >
      Welcome to React
    </h1>
  </header>
  <p
    className="App-intro"
  >
    To get started, edit
    <code>
      src/App.js
    </code>
     and save to reload.
  </p>
</div>
`

كما ترى أنه الرمز الذي يعرضه مكون التطبيق ، لا شيء أكثر من ذلك.

في المرة التالية التي يقارن فيها الاختبار إخراج<App />الى هذا. إذا تغير التطبيق ، فستتلقى خطأ:

Error with snapshot

عند استخدامyarn testفيcreate-react-appأنت فيوضع المشاهدة، ومن هناك يمكنك الضغطwوأظهر المزيد من الخيارات:

Watch Usage
 › Press u to update failing snapshots.
 › Press p to filter by a filename regex pattern.
 › Press t to filter by a test name regex pattern.
 › Press q to quit watch mode.
 › Press Enter to trigger a test run.

If your change is intended, pressing u will update the failing snapshots, and make the test pass.

You can also update the snapshot by running jest -u (or jest --updateSnapshot) outside of watch mode.


More devtools tutorials: