اختبار تطبيقات Next.js
نلقي نظرة في هذه الصفحة على طرق اختبار تطبيقات Next.js باستخدادم أدوات مثل Cypress و Playwright و Jest مع مكتبة اختبار React.
اختبار تطبيقات Next.js باستخدام Cypress
تُستخدم المكتبة Cypress في تنفيذ اختبارات شاملة End-to-End Testing -أو E2E اختصارًا- واختبارات التكامل Integration Testing.
بداية سريعة
بإمكانك استخدام create-next-app
مع المثال with-cypress لتبدأ العمل بسرعة:
npx create-next-app@latest --example with-cypress with-cypress-app
الإعداد اليدوي
ثبّت حزمة cypress
كالتالي:
npm install --save-dev cypress
أضف Cypress إلى حقل السكربتات "scripts" في ملف package.json
:
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"cypress": "cypress open",
}
شغّل Cypress للمرة الأولى لكي تولِّد أمثلة تستخدم الهيكلية المفضّلة للمجلد الخاص بها:
npm run cypress
يمكنك الاطلاع على الأمثلة الموّلدة وتوثيق Cypress لتألف التعامل مع هذ المكتبة.
إنشاء أول اختبار تكامل باستخدام Cypress
لنفترض وجود صفحتي Next.js التاليتين:
// pages/index.js
import Link from 'next/link'
export default function Home() {
return (
<nav>
<Link href="/about">
<a>About</a>
</Link>
</nav>
)
}
// pages/about.js
export default function About() {
return (
<div>
<h1>About Page</h1>
</div>
)
}
أضف اختبارًا للتحقق من إمكانية التنقل بينهما:
// cypress/integration/app.spec.js
describe('Navigation', () => {
it('should navigate to the about page', () => {
// index ابدأ بالصفحة
cy.visit('http://localhost:3000/')
//ثم انقر عليها "about" التي تضم href جد رابطًا له السمة
cy.get('a[href*="about"]').click()
//"/about" ينبغي أن يضم العنوان الجدبد
cy.url().should('include', '/about')
//"About page" مع h1 ينبغي أن تضم الصفحة الجديدة العنصر
cy.get('h1').contains('About Page')
})
})
بإمكانك استخدام ("/")cy.visit
بدلًا من cy.visit("http://localhost:3000/")
، إن أضفت "baseUrl": "http://localhost:3000"
إلى ملف التهيئة cypress.json
.
تنفيذ اختبارات Cypress
تختبر Cypress تطبيقات Next.js حقيقية لذلك لا بد أن يعمل خادم Next.js قبل أن تبدأ Cypress. ننصحك بتطبيق اختباراتك على شيفرة الإنتاج لتقف بشكل أوضح على سلوك تطبيقك.
نفّذ الأمر npm run build
ثم npm run start
وبعد ذلك الأمر npm run cypress
في نافذة جديدة للطرفية لتشغيل Cypress.
ملاحظة: بإمكانك كحل بديل أن تُثبِّت الحزمة start-server-and-test
وتضيفها إلى حقل السكربتات "scripts" في ملف package.json
كالتالي:
"test": "start-server-and-test start http://localhost:3000 cypress"
وذلك كي تُشغّل خادم إنتاج Next.js إلى جوار Cypress، وتذكرأن تعيد بناء التطبيق بعد إجراء تعديلات جديدة.
التحضير لعملية التكامل المتواصل CI في تطبيقات Next.js
ربما ستلاحظ أن Cypress تشغل متصفحًا تفاعليًا ليس نموذجيًا لبيئة التكامل المتواصل Continuous Integration. بإمكانك أن تُشغّل Cypress أيضًا دون واجهة المتصفح تلك بتنفيذ الأمر cypress run
:
// package.json
"scripts": {
//...
"cypress": "cypress open",
"cypress:headless": "cypress run",
"e2e": "start-server-and-test start http://localhost:3000 cypress",
"e2e:headless": "start-server-and-test start http://localhost:3000 cypress:headless"
}
يمكنك الاطلاع على المزيد من المعلومات حول Cypress والتتكامل المتواصل من خلال المصادر التالية:
اختبار تطبيقات Next.js باستخدام Playwright
Playwright هو إطار عمل للاختبارات يتيح لك أتمتة Chromium و Firefox و WebKit من خلال واجهة برمجية واحدة. يُمكنك استخدام Playwright في تنفيذ الاختبارات الشاملة End-to-End واختبارات التكامل Integration عبر جميع المنصات.
بداية سريعة
بإمكانك استخدام create-next-app
مع المثال with-playwright لتبدأ العمل بسرعة. سيُنشئ ذلك مشروعًا جديدًا أعدت فيه Next.js بشكل كامل.
npx create-next-app@latest --example with-playwright with-playwright-app
الإعداد اليدوي
بإمكانك أن تستخدم npm init playwright
أيضًا لإضافة Playwright إلى مشروع NPM
موجود مسبقًا. ثبِّت الحزمة playwright/test@
لكي تبدأ يدويًا العمل مع Playwright.
npm install --save-dev @playwright/test
أضف Playwright إلى حقل "scripts" في الملف package.json
:
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"test:e2e": "playwright test",
}
إنشاء أول اختبار مشترك للواجهتين باستخدام Playwright
افترض وجود صفحتي Next.js التاليتين:
// pages/index.js
import Link from 'next/link'
export default function Home() {
return (
<nav>
<Link href="/about">
<a>About</a>
</Link>
</nav>
)
}
// pages/about.js
export default function About() {
return (
<div>
<h1>About Page</h1>
</div>
)
}
أضف اختبارًا يتحقق من إمكانية التنقل بين الصفحتين:
// e2e/example.spec.ts
import { test, expect } from '@playwright/test'
test('should navigate to the about page', async ({ page }) => {
// يُحدد العنوان الجذري من قبل خادم ويب وفقًا للملف) index ابدأ من الصفحة
// the playwright.config.ts)
await page.goto('http://localhost:3000/')
//وانقر عليه 'About Page' جد عنصرًا يحمل النص
await page.click('text=About Page')
//(باستخدام العنوان الجذري) "/about" الجديد هو URL ينبغي أن يكون عنوان
await expect(page).toHaveURL('http://localhost:3000/about')
//"About page" مع h1 ينبغي أن تضم الصفحة الجديدة العنصر
await expect(page.locator('h1')).toContainText('About Page')
})
بإمكانك استخدام ("/")page.goto
بدلًا من page.goto("http://localhost:3000/")
، إن أضفت "baseUrl": "http://localhost:3000"
إلى ملف التهيئة playwright.config.ts
.
تنفيذ اختبارات Playwright
تختبر Playwright تطبيقات Next.js حقيقية لذلك لا بد أن يعمل خادم Next.js قبل أن تبدأ Playwright. ننصحك بتطبيق اختباراتك على شيفرة الإنتاج لتقف بشكل أوضح على سلوك تطبيقك.
نفّذ الأمر npm run build
ثم npm run start
وبعد ذلك الأمر npm run test:e2e
في نافذة جديدة للطرفية لتشغيل Playwright.
ملاحظة: بإمكانك كحل بديل استخدام الميزة webServer
لكي تسمح للمكتبة Playwright بتشغيل خادم التطوير ومن ثم تنتظر حتى يجهز تمامًا.
تشغيل Playwright في وضع التكامل المتواصل
تُنفَّذ Playwright اختباراتك دون واجهة headless mode افتراضيًا. ولتثبيت كل اعتماديات Playwright نفّذ الأمر npx playwright install-deps
بإمكانك الاطلاع أكثر على Playwright والتكامل المتواصل من خلال المصادر التالية:
- تجهّز للعمل مع Playwright.
- استخدام خادم التطوير.
- استخدام Playwright مع مزوّد التكامل المتواصل الخاص بك.
اختبار تطبيقات Next.js باستخدام Jest ومكتبة اختبار React
تُستسخدم هاتين المكتبتين مرارًا في إجراء اختبار الوحدات Unit Testing. وهنالك ثلاث طرق لكي تستخدم Jest في تطبيق Next.js:
- استخدام أحد أمثلة الإقلاع السريع.
- مع مُصرَّف Rust الخاص بتطبيقات Next.js.
- باستخدام Babel.
بداية سريعة
بإمكانك استخدام create-next-app
مع المثال with-jest لتبدأ العمل بسرعة:
npx create-next-app@latest --example with-jest with-jest-app
إعداد المكتبة Jest (مع مُصرّف Rust)
تتمتع Next.js ابتداءً من النسخة 12 بدعم مدمج للمكتبة Jest. ولإعداد Jest، ثبِّت الاعتماديات التالية jest
و @testing-library/react
و @testing-library/jest-dom
:
npm install --save-dev jest @testing-library/react @testing-library/jest-dom
أنشئ الملف jest.config.js
في جذر مجلد مشروعك وأضف الشيفرة التالية:
// jest.config.js
const nextJest = require('next/jest')
const createJestConfig = nextJest({
//في بيئة التطوير .env وملفات next.config.js ضع مسار تطبيقك لتحميل
dir: './',
})
// Jest أضف أية إعدادات أخرى تنوي تمريرها إلى
const customJestConfig = {
// أضف أية خيارات إعداد أخرى قبل تنفيذ كل اختبار
// setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
// ستحتاج إلى مايلي لإخفاء العمل baseUrl مع عناوين جذرية TypeScript إن كنت تستخدم
moduleDirectories: ['node_modules', '<rootDir>/'],
testEnvironment: 'jest-environment-jsdom',
}
//قادر على next/jest بهذا الشكل للتأكد من أن createJestConfig تُصدّر
//بشكل غير متزامن Next.js تحميل إعدادات
module.exports = createJestConfig(customJestConfig)
تهيئ next/jest
المكتبة Jest خلف الستار نيابة عنك بما في ذلك:
- إعداد
transform
باستخدام SWC. - التقليد التلقائي للتنسيقات (
css.
وmodule.css.
وما يماثلها من تنسيقاتSCSS
). - تحميل
env.
(وكل ما يماثلها) ضمنprocess.env
. - تجاهل
node_modules
عند تحليل وتنفيذ الاختبارات والتحويلات transforms. - تجاهل
next.
عند تحليل الاختبار. - تحميل
next.config.js
من أجل الرايات التي تفعّل تحويلات SWC.
ملاحظة: إن أردت اختبار متغيرات البيئة مباشرةً، فحملهم يدويًا في سكربت تهيئة منفصل أو في ملف jest.config.js، ولمزيد من التفاصيل، ارجع إلى قسم اختبار متغيرات البيئة.
ضبط إعدادات Jest مع Babel
إن أردت الاستغناء عن مُصرِّف Rust، لا بد من ضبط إعدادات Babel يدويًا وتثبيت babel-jest
و identity-obj-proxy
بالإضافة إلى الحزم التي ثبتها سابقًا.
إليك الخيارات المستحسنة لتهيئة Jest من أجل تطبيقات Next.js:
// jest.config.js
module.exports = {
collectCoverage: true,
coverageProvider: 'v8',
collectCoverageFrom: [
'**/*.{js,jsx,ts,tsx}',
'!**/*.d.ts',
'!**/node_modules/**',
'!<rootDir>/out/**',
'!<rootDir>/.next/**',
'!<rootDir>/*.config.js',
'!<rootDir>/coverage/**',
],
moduleNameMapper: {
// (CSS مع وحدات) المُدرج CSS التعامل مع تنسيقات
// https://jestjs.io/docs/webpack#mocking-css-modules
'^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy',
//(CSS دون وحدات) المُدرج CSS التعامل مع تنسيقات
// Handle CSS imports (without CSS modules)
'^.+\\.(css|sass|scss)$': '<rootDir>/__mocks__/styleMock.js',
// التعامل مع إدراج الصور
// https://jestjs.io/docs/webpack#handling-static-assets
'^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i': `<rootDir>/__mocks__/fileMock.js`,
// التعامل مع الوحدات البديلة (المقنعة أو المخفية)
'^@/components/(.*)$': '<rootDir>/components/$1',
},
// أضف أية خيارات إعداد أخرى قبل تنفيذ كل اختبار
// setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/.next/'],
testEnvironment: 'jsdom',
transform: {
//next/babel لنقل شيفرة الاختبارات باستخدام babel-jest استخدام
// https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object
'^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }],
},
transformIgnorePatterns: [
'/node_modules/',
'^.+\\.module\\.(css|sass|scss)$',
],
}
بإمكانك الاطلاع على كل خيار من خيارات الإعداد في توثيق Jest.
إدراج الصور وصفحات التسيق CSS
لا تُستخدم الصور والتنسيقات في الاختبارات لكن إدراجها قد يقود إلى أخطاء، وبالتالي لا بد من تقليدها. انشئ ملفات التقليد mock files المُشار إليها في الإعدادات السابقة (fileMock.js
و styleMock.js
) ضمن المجلد __mocks__
:
// __mocks__/fileMock.js
module.exports = {
src: '/img.jpg',
height: 24,
width: 24,
blurDataURL: 'data:image/png;base64,imagedata',
}
// __mocks__/styleMock.js
module.exports = {}
لمعلومات أكثر عن التعامل مع الموجودات أو الأصول الثابتة static assets راجع توثيق Jest.
توسعة Jest بمطابقات مخصصة (اختياري)
تتضمن testing-library/jest-dom@
مجموعة من المُطابِقات المخصصة custom matchers التي تُسهَّل كتابة الاختبارات مثل ()toBeInTheDocument.
. بإمكانك إدراج المُطابق المخصص في كل اختبار بإضافة الخيار التالي إلى ملف تهيئة Jest:
// jest.config.js
setupFilesAfterEnv: ['<rootDir>/jest.setup.js']
ثم أدرج ما يلي داخل الملف jest.setup.js
:
// jest.setup.js
import '@testing-library/jest-dom/extend-expect'
إن أردت إضافة المزيد من خيارات الإعداد قبل كل اختبار، فمن الشائع إضافتها داخل الملف jest.setup.js
.
الإدراجات ذات المسارات المطلقة والبديلة
إن استخدمت بدائل مسار الوحدة البرمجية Module Path Aliases، فلا بد من تهيئة Jest ليحلل هذه الإدراجات بمطابقة الخيار "paths" (المسارات) في الملف jsconfig.json
مع الخيار moduleNameMapper
في الملف jest.config.js
. إليك مثالًا:
// tsconfig.json or jsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/components/*": ["components/*"]
}
}
}
// jest.config.js
moduleNameMapper: {
'^@/components/(.*)$': '<rootDir>/components/$1',
}
إنشاء الاختبارات الخاصة بك
- إضافة سكربت الاختبار إلى الملف package.json: أضف خيار تنفيذ اختبارات Jest إلى سكربتات
package.json
، إذ يعيد الخيار تشغيل الاختبار عندما يتغير الملف:
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"test": "jest --watch"
}
- إنشاء أول اختباراتك: أصبح مشروعك الآن جاهزًا لتنفيذ الاختبارات. اتبع تقاليد Jest بإضافة الاختبارات إلى المجلد
__tests__
في جذر مجلد مشروعك. يمكنك مثلًا إضافة اختبار يتحقق من تصيير المكوّن</ Home>
لعنوان بشكل صحيح:
// __tests__/index.test.jsx
import { render, screen } from '@testing-library/react'
import Home from '../pages/index'
import '@testing-library/jest-dom'
describe('Home', () => {
it('renders a heading', () => {
render(<Home />)
const heading = screen.getByRole('heading', {
name: /welcome to next\.js!/i,
})
expect(heading).toBeInTheDocument()
})
})
وكخيار آخر، أضف اختبار اللقطات snapshot test لتعقب التغيرات غير المتوقعة في المكوّن </ Home>
// __tests__/snapshot.js
import { render } from '@testing-library/react'
import Home from '../pages/index'
it('renders homepage unchanged', () => {
const { container } = render(<Home />)
expect(container).toMatchSnapshot()
})
ملاحظة: لا ينبغي وضع ملفات الاختبار مباشرة ضمن مجلد الصفحات لأن كل ملف في هذا المجلد يُعدّث وجهة route.
- تشغيل مجموعة الاختبارات: نفّذ ببساطة الأمر
npm run test
وستلاحظ مجموعة من الأوامر التفاعلية يولّدها Jest سواء أخفق أو نجح الاختبار، ولهذا فائدته عند إضافة اختبارات أكثر.
لمعلومات أكثر، يمكنك الاطلاع على المصادر التالية:
- توثيق Jest
- توثيق مكتبة اختبارت React
- Testing Playground: وهي منصة تدرّبك على إجراء الاختبارات ومطابقة العناصر.
حزم طوّرها مجتمع Next.js وأمثلة متنوعة
طوّر مجتمع Next.js حزمًا قد تجدها مفيدة منها:
- next-page-tester: لمكاملة شجرة DOM مع الاختبارات.
- next-router-mock: من أجل Storybook.
أمثلة
- Next.js with Cypress
- Next.js with Playwright
- Next.js with Jest and React Testing Library
- Next.js with Vitest
المصادر
- الصفحة Testing من توثيق Next.js الرسمي.