الاختبار
أمثلة
تعلم كيفية إعداد Next.js مع أدوات الاختبار الشائعة: Cypress، Playwright، وJest مع مكتبة React Testing Library.
Cypress
Cypress هو مشغل اختبار يُستخدم لـاختبار النهاية إلى النهاية (E2E) واختبار المكونات.
البدء السريع
يمكنك استخدام create-next-app
مع مثال with-cypress للبدء بسرعة.
npx create-next-app@latest --example with-cypress with-cypress-app
الإعداد اليدوي
للبدء مع Cypress، قم بتثبيت حزمة cypress
:
npm install --save-dev cypress
أضف Cypress إلى حقل scripts
في package.json
:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"cypress:open": "cypress open"
}
}
شغّل Cypress لأول مرة لإنشاء أمثلة تستخدم هيكل المجلدات الموصى به:
npm run cypress:open
يمكنك الاطلاع على الأمثلة المُنشأة وقسم كتابة أول اختبار لك في وثائق Cypress للتعرف على كيفية استخدامه.
هل يجب استخدام اختبارات E2E أم اختبارات المكونات؟
تحتوي وثائق Cypress على دليل يوضح الفرق بين هذين النوعين من الاختبارات ومتى يكون كل منهما مناسبًا للاستخدام.
إنشاء أول اختبار E2E باستخدام Cypress
بافتراض وجود صفحتي Next.js التاليتين:
import Link from 'next/link'
export default function Home() {
return (
<nav>
<h1>الصفحة الرئيسية</h1>
<Link href="/about">حول</Link>
</nav>
)
}
export default function About() {
return (
<div>
<h1>صفحة حول</h1>
<Link href="/">الصفحة الرئيسية</Link>
</div>
)
}
أضف اختبارًا للتحقق من عمل التنقل بشكل صحيح:
describe('التنقل', () => {
it('يجب الانتقال إلى صفحة حول', () => {
// ابدأ من الصفحة الرئيسية
cy.visit('http://localhost:3000/')
// ابحث عن رابط يحتوي على "about" وانقر عليه
cy.get('a[href*="about"]').click()
// يجب أن يتضمن الرابط الجديد "/about"
cy.url().should('include', '/about')
// يجب أن تحتوي الصفحة الجديدة على h1 بعنوان "صفحة حول"
cy.get('h1').contains('صفحة حول')
})
})
يمكنك استخدام cy.visit("/")
بدلاً من cy.visit("http://localhost:3000/")
إذا أضفت baseUrl: 'http://localhost:3000'
إلى ملف تكوين cypress.config.js
.
إنشاء أول اختبار مكونات باستخدام Cypress
تقوم اختبارات المكونات ببناء وتثبيت مكون معين دون الحاجة إلى تجميع التطبيق بالكامل أو تشغيل خادم. هذا يسمح باختبارات أكثر كفاءة مع توفير نفس واجهة برمجة التطبيقات المستخدمة في اختبارات E2E.
معلومة مفيدة: نظرًا لأن اختبارات المكونات لا تشغل خادم Next.js، فإن ميزات مثل
<Image />
وgetServerSideProps
التي تعتمد على وجود خادم لن تعمل افتراضيًا. راجع وثائق Cypress لـ Next.js للحصول على أمثلة حول جعل هذه الميزات تعمل في اختبارات المكونات.
بافتراض نفس المكونات من القسم السابق، أضف اختبارًا للتحقق من عرض المكون للنتائج المتوقعة:
import AboutPage from './about.js'
describe('<AboutPage />', () => {
it('يجب عرض المحتوى المتوقع', () => {
// ثبّت مكون React لصفحة حول
cy.mount(<AboutPage />)
// يجب أن تحتوي الصفحة الجديدة على h1 بعنوان "صفحة حول"
cy.get('h1').contains('صفحة حول')
// تحقق من وجود رابط بالمسار المتوقع
// *اتباع* الرابط أكثر ملاءمة لاختبار E2E
cy.get('a[href="/"]').should('be.visible')
})
})
تشغيل اختبارات Cypress
اختبارات E2E
نظرًا لأن اختبارات E2E في Cypress تختبر تطبيق Next.js حقيقي، فإنها تتطلب تشغيل خادم Next.js قبل بدء Cypress. نوصي بتشغيل الاختبارات ضد كود الإنتاج لمحاكاة سلوك التطبيق الفعلي.
قم بتشغيل npm run build
ثم npm run start
، ثم شغّل npm run cypress -- --e2e
في نافذة طرفية أخرى لبدء Cypress وتشغيل مجموعة اختبارات E2E.
معلومة مفيدة: بدلاً من ذلك، يمكنك تثبيت حزمة
start-server-and-test
وإضافتها إلى حقلscripts
فيpackage.json
:"test": "start-server-and-test start http://localhost:3000 cypress"
لبدء خادم إنتاج Next.js بالتزامن مع Cypress. تذكر إعادة بناء التطبيق بعد أي تغييرات جديدة.
اختبارات المكونات
شغّل npm run cypress -- --component
لبدء Cypress وتنفيذ مجموعة اختبارات المكونات.
التحضير للتكامل المستمر (CI)
ستلاحظ أن تشغيل Cypress حتى الآن فتح متصفحًا تفاعليًا وهو غير مثالي لبيئات CI. يمكنك أيضًا تشغيل Cypress بدون واجهة مرئية باستخدام أمر cypress run
:
{
"scripts": {
//...
"e2e": "start-server-and-test dev http://localhost:3000 \"cypress open --e2e\"",
"e2e:headless": "start-server-and-test dev http://localhost:3000 \"cypress run --e2e\"",
"component": "cypress open --component",
"component:headless": "cypress run --component"
}
}
يمكنك معرفة المزيد عن Cypress والتكامل المستمر من هذه المصادر:
- وثائق التكامل المستمر لـ Cypress
- دليل Cypress لـ GitHub Actions
- إجراء Cypress الرسمي لـ GitHub
- Cypress Discord
Playwright
Playwright هو إطار اختبار يتيح لك أتمتة Chromium وFirefox وWebKit باستخدام واجهة برمجة تطبيقات واحدة. يمكنك استخدامه لكتابة اختبارات النهاية إلى النهاية (E2E) والتكامل عبر جميع المنصات.
البدء السريع
أسرع طريقة للبدء هي استخدام create-next-app
مع مثال with-playwright. سيؤدي هذا إلى إنشاء مشروع Next.js مكتمل مع إعداد Playwright بالكامل.
npx create-next-app@latest --example with-playwright with-playwright-app
الإعداد اليدوي
يمكنك أيضًا استخدام npm init playwright
لإضافة Playwright إلى مشروع NPM
موجود.
للبدء يدويًا مع Playwright، قم بتثبيت حزمة @playwright/test
:
npm install --save-dev @playwright/test
أضف Playwright إلى حقل scripts
في package.json
:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"test:e2e": "playwright test"
}
}
إنشاء أول اختبار E2E باستخدام Playwright
بافتراض وجود صفحتي Next.js التاليتين:
import Link from 'next/link'
export default function Home() {
return (
<nav>
<Link href="/about">حول</Link>
</nav>
)
}
export default function About() {
return (
<div>
<h1>صفحة حول</h1>
</div>
)
}
أضف اختبارًا للتحقق من عمل التنقل بشكل صحيح:
import { test, expect } from '@playwright/test'
test('يجب الانتقال إلى صفحة حول', async ({ page }) => {
// ابدأ من الصفحة الرئيسية (يتم تعيين baseURL عبر webServer في ملف playwright.config.ts)
await page.goto('http://localhost:3000/')
// ابحث عن عنصر يحتوي على نص 'حول' وانقر عليه
await page.click('text=حول')
// يجب أن يكون الرابط الجديد "/about" (يتم استخدام baseURL هنا)
await expect(page).toHaveURL('http://localhost:3000/about')
// يجب أن تحتوي الصفحة الجديدة على h1 بعنوان "صفحة حول"
await expect(page.locator('h1')).toContainText('صفحة حول')
})
import { test, expect } from '@playwright/test'
test('يجب الانتقال إلى صفحة حول', async ({ page }) => {
// ابدأ من الصفحة الرئيسية (يتم تعيين baseURL عبر webServer في ملف playwright.config.ts)
await page.goto('http://localhost:3000/')
// ابحث عن عنصر يحتوي على نص 'حول' وانقر عليه
await page.click('text=حول')
// يجب أن يكون الرابط الجديد "/about" (يتم استخدام baseURL هنا)
await expect(page).toHaveURL('http://localhost:3000/about')
// يجب أن تحتوي الصفحة الجديدة على h1 بعنوان "صفحة حول"
await expect(page.locator('h1')).toContainText('صفحة حول')
})
يمكنك استخدام 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 على التكامل المستمر (CI)
سيقوم Playwright افتراضيًا بتشغيل اختباراتك في وضع headless. لتثبيت جميع تبعيات Playwright، شغّل npx playwright install-deps
.
يمكنك معرفة المزيد عن Playwright والتكامل المستمر من هذه المصادر:
Jest ومكتبة React Testing Library
يتم استخدام Jest ومكتبة React Testing Library معًا بشكل متكرر لـاختبار الوحدة. هناك ثلاث طرق يمكنك من خلالها البدء باستخدام Jest داخل تطبيق Next.js الخاص بك:
- استخدام أحد أمثلة البدء السريع
- مع مُجمّع Rust لـ Next.js
- مع Babel
ستغطي الأقسام التالية كيفية إعداد Jest مع كل من هذه الخيارات:
البدء السريع
يمكنك استخدام create-next-app
مع مثال with-jest للبدء بسرعة مع Jest ومكتبة React Testing Library:
npx create-next-app@latest --example with-jest with-jest-app
إعداد Jest (مع مُجمّع Rust)
منذ إصدار Next.js 12، أصبح لدى Next.js الآن تكوين مدمج لـ Jest.
لإعداد Jest، قم بتثبيت jest
، jest-environment-jsdom
، @testing-library/react
، @testing-library/jest-dom
:
npm install --save-dev jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom
قم بإنشاء ملف jest.config.mjs
في الدليل الجذري لمشروعك وأضف ما يلي:
import nextJest from 'next/jest.js'
const createJestConfig = nextJest({
// حدد مسار تطبيق Next.js لتحميل next.config.js وملفات .env في بيئة الاختبار
dir: './',
})
// أضف أي تكوين مخصص لتمريره إلى Jest
/** @type {import('jest').Config} */
const config = {
// أضف خيارات إعداد إضافية قبل تشغيل كل اختبار
// setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
testEnvironment: 'jest-environment-jsdom',
}
// يتم تصدير createJestConfig بهذه الطريقة لضمان أن next/jest يمكنه تحميل تكوين Next.js غير المتزامن
export default createJestConfig(config)
تحت الغطاء، يقوم next/jest
بتكوين Jest تلقائيًا لك، بما في ذلك:
- إعداد
transform
باستخدام SWC - المحاكاة التلقائية لملفات الأنماط (
.css
،.module.css
، ومتغيرات scss الخاصة بها)، واستيراد الصور وnext/font
- تحميل
.env
(وجميع المتغيرات) إلىprocess.env
- تجاهل
node_modules
من تحويلات الاختبار - تجاهل
.next
من تحويلات الاختبار - تحميل
next.config.js
للعلامات التي تمكّن تحويلات SWC
معلومة مفيدة: لاختبار متغيرات البيئة مباشرة، قم بتحميلها يدويًا في سيناريو إعداد منفصل أو في ملف
jest.config.js
. لمزيد من المعلومات، راجع متغيرات بيئة الاختبار.
إعداد جيست (مع Babel)
إذا اخترت عدم استخدام مُصرِّف Rust، فستحتاج إلى تكوين جيست يدويًا وتثبيت babel-jest
و identity-obj-proxy
بالإضافة إلى الحزم المذكورة أعلاه.
إليك الخيارات الموصى بها لتكوين جيست لـ Next.js:
module.exports = {
collectCoverage: true,
// في node 14.x، موفر التغطية v8 يوفر سرعة جيدة وتقريرًا جيدًا إلى حد ما
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)
'^.+\\.(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: {
// استخدام babel-jest لتحويل الاختبارات مع الإعداد المسبق next/babel
// 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)$',
],
}
يمكنك معرفة المزيد حول كل خيار تكوين في وثائق جيست.
التعامل مع صفحات الأنماط واستيراد الصور
لا تُستخدم صفحات الأنماط والصور في الاختبارات، ولكن استيرادها قد يتسبب في أخطاء، لذا سيحتاج إلى محاكاتها. أنشئ ملفات المحاكاة المشار إليها في التكوين أعلاه - fileMock.js
و styleMock.js
- داخل مجلد __mocks__
:
module.exports = {
src: '/img.jpg',
height: 24,
width: 24,
blurDataURL: 'data:image/png;base64,imagedata',
}
module.exports = {}
لمزيد من المعلومات حول التعامل مع الأصول الثابتة، يُرجى الرجوع إلى وثائق جيست.
اختياري: توسيع جيست مع مطابقات مخصصة
يتضمن @testing-library/jest-dom
مجموعة من المطابقات المخصصة الملائمة مثل .toBeInTheDocument()
مما يجعل كتابة الاختبارات أسهل. يمكنك استيراد المطابقات المخصصة لكل اختبار عن طريق إضافة الخيار التالي إلى ملف تكوين جيست:
setupFilesAfterEnv: ['<rootDir>/jest.setup.js']
ثم، داخل jest.setup.js
، أضف الاستيراد التالي:
import '@testing-library/jest-dom'
تمت إزالة
extend-expect
في الإصدارv6.0
، لذا إذا كنت تستخدم@testing-library/jest-dom
قبل الإصدار 6، فستحتاج إلى استيراد@testing-library/jest-dom/extend-expect
بدلاً من ذلك.
إذا كنت بحاجة إلى إضافة المزيد من خيارات الإعداد قبل كل اختبار، فمن الشائع إضافتها إلى ملف jest.setup.js
أعلاه.
اختياري: الاستيراد المطلق وأسماء مسارات الوحدات
إذا كان مشروعك يستخدم أسماء مسارات الوحدات، فستحتاج إلى تكوين جيست لحل الاستيراد عن طريق مطابقة خيار المسارات في ملف jsconfig.json
مع خيار moduleNameMapper
في ملف jest.config.js
. على سبيل المثال:
{
"compilerOptions": {
"module": "esnext",
"moduleResolution": "node",
"baseUrl": "./",
"paths": {
"@/components/*": ["components/*"]
}
}
}
moduleNameMapper: {
'^@/components/(.*)$': '<rootDir>/components/$1',
}
إنشاء اختباراتك:
إضافة سكريبت اختبار إلى package.json
أضف تنفيذ جيست في وضع المراقبة إلى سكريبتات package.json
:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"test": "jest --watch"
}
}
سيقوم jest --watch
بإعادة تشغيل الاختبارات عند تغيير ملف. لمزيد من خيارات واجهة سطر أوامر جيست، يُرجى الرجوع إلى وثائق جيست.
إنشاء أول اختباراتك
مشروعك جاهز الآن لتشغيل الاختبارات. اتبع اصطلاح جيست بإضافة الاختبارات إلى مجلد __tests__
في الدليل الجذري لمشروعك.
على سبيل المثال، يمكننا إضافة اختبار للتحقق مما إذا كان مكون <Home />
يعرض عنوانًا بنجاح:
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()
})
})
اختياريًا، أضف اختبار لقطة لتتبع أي تغييرات غير متوقعة في مكون <Home />
:
import { render } from '@testing-library/react'
import Home from '../pages/index'
it('renders homepage unchanged', () => {
const { container } = render(<Home />)
expect(container).toMatchSnapshot()
})
معلومة جيدة: لا يجب تضمين ملفات الاختبار داخل موجه الصفحات لأن أي ملف داخل موجه الصفحات يعتبر مسارًا.
تشغيل مجموعة الاختبارات الخاصة بك
شغّل npm run test
لتشغيل مجموعة الاختبارات الخاصة بك. بعد نجاح أو فشل اختباراتك، ستلاحظ قائمة بأوامر جيست التفاعلية التي ستكون مفيدة عند إضافة المزيد من الاختبارات.
لمزيد من القراءة، قد تجد هذه المصادر مفيدة:
- وثائق جيست
- وثائق مكتبة اختبار React
- ساحة الاختبار - استخدم ممارسات اختبار جيدة لمطابقة العناصر.
حزم وأمثلة المجتمع
قام مجتمع Next.js بإنشاء حزم ومقالات قد تجدها مفيدة:
- next-router-mock لـ Storybook.
- اختبار معاينة نشرات Vercel مع Cypress بواسطة Gleb Bahmutov.
لمزيد من المعلومات حول ما يمكن قراءته بعد ذلك، نوصي بـ:
- pages/basic-features/environment-variables#test-environment-variables