تُعدّ تدفقات البريد الإلكتروني من أصعب أجزاء تطبيق الويب اختباراً بشكل صحيح. تأكيدات التسجيل، وإعادة تعيين كلمات المرور، ورسائل البريد الإلكتروني التحويلية، وتسلسلات الإعداد — كلها تتطلب صندوق وارد يعمل للتحقق منها.
استخدام عناوين بريد إلكتروني حقيقية لهذا الغرض فكرة سيئة: إذ يُربك صناديق الوارد، ويخاطر بإرسال رسائل اختبار إلى مستخدمين حقيقيين، ولا يتوسع للاختبار الآلي. عناوين البريد الإلكتروني المؤقتة والقابلة للتخلص تحل هذه المشكلات الثلاث.
يغطي هذا الدليل مجموعة الأدوات الكاملة — من الاختبار اليدوي السريع باستخدام صندوق بريد مؤقت إلى التحقق الآلي الكامل من البريد الإلكتروني في خطوط أنابيب CI/CD.
المشكلة الجوهرية في اختبار البريد الإلكتروني
عند بناء تدفق بريد إلكتروني، تحتاج إلى التحقق من:
- تم إرسال البريد الإلكتروني فعلاً
- تم تسليمه (دون ارتداد أو اصطياد بفلاتر الرسائل غير المرغوب فيها)
- المحتوى صحيح (الموضوع والنص والروابط)
- الروابط في البريد الإلكتروني تعمل بشكل صحيح (روابط التأكيد وتوكنات إعادة تعيين كلمة المرور)
- التدفق يكتمل بشكل صحيح بعد النقر على الرابط
القيام بذلك باستخدام عناوين بريد إلكتروني حقيقية يخلق عدة مشكلات:
- تلوث صندوق الوارد — تمتلئ صناديق بريد فريقك بمئات من رسائل الاختبار
- الإرسال العرضي إلى مستخدمين حقيقيين — متغير بيئة مُهيَّأ بشكل خاطئ وستذهب رسائل الاختبار إلى مستخدمي الإنتاج
- لا وصول برمجي — لا يمكنك قراءة صندوق بريد حقيقي من خط أنابيب CI/CD دون OAuth أو تهيئة IMAP
- حلقات تغذية راجعة بطيئة — التحقق يدوياً من صندوق بريد حقيقي لكل تشغيل اختبار لا يتوسع
فئات الأدوات لاختبار البريد الإلكتروني
هناك أربعة مناهج رئيسية، كل منها مناسب لمواقف مختلفة:
1. صناديق الوارد العامة القابلة للتخلص (الاختبار اليدوي)
خدمات مثل InstantTempEmail وGuerrilla Mail و10 Minute Mail تمنحك صندوق وارد يعمل على الفور. استخدمها عندما تختبر تدفقاً يدوياً وتحتاج فقط إلى استقبال بريد إلكتروني تحقق بسرعة.
متى تستخدمها:
- اختبار التطوير المحلي لتدفق بريد إلكتروني جديد
- فحوصات QA الفردية أثناء التطوير
- اختبار كيفية عرض بريدك الإلكتروني في صندوق وارد حقيقي
متى لا تستخدمها:
- الاختبارات الآلية — لا يمكنك قراءة هذه الصناديق برمجياً في مجموعة اختبارات
2. أجهزة اصطياد SMTP للتطوير (آلية، معزولة)
تعترض هذه الأدوات جميع رسائل البريد الإلكتروني الصادرة في بيئة التطوير أو التجهيز — لا يُسلَّم شيء فعلياً إلى عناوين حقيقية. تفحص رسائل البريد المُلتقطة عبر واجهة ويب أو API.
Mailtrap هو الأكثر استخداماً. تُهيئ تطبيقك للإرسال عبر خادم SMTP الخاص به في البيئات غير الإنتاجية. تذهب جميع رسائل البريد إلى صندوق وارد sandbox.
MailHog هو بديل ذاتي الاستضافة — تطبيق Go خفيف الوزن يشغّل خادم SMTP وواجهة ويب محلياً. صفر تكلفة، صفر تبعية خارجية.
Mailpit هو بديل أحدث لـ MailHog بواجهة أفضل وتطوير نشط.
3. صناديق الوارد العامة عبر API (آلية، مع وصول للصندوق)
خدمات مثل Mailinator توفر صناديق وارد عامة يمكن الوصول إليها عبر HTTP API. ترسل بريداً إلكترونياً إلى testuser@mailinator.com من تطبيقك، ثم تفحص الصندوق عبر API الخاصة بهم في مجموعة الاختبارات.
متى تستخدمها:
- اختبارات من البداية إلى النهاية تحتاج إلى التحقق من محتوى البريد الإلكتروني والنقر على الروابط
- خطوط أنابيب CI/CD حيث تحتاج إلى وصول للصندوق دون OAuth
- اختبار التدفقات المُشغَّلة بالبريد الإلكتروني (التأكيد → إعادة التوجيه → الإعداد)
4. خدمات اختبار البريد الإلكتروني التحويلي
خدمات مثل Mailtrap (منتج الاختبار الخاص بهم، منفصل عن sandbox) أو وضع اختبار Postmark أو وضع sandbox الخاص بـ SendGrid تختبر خط أنابيب الإرسال الكامل — استدعاءات API والعرض والتسليم — دون إرسال فعلي.
إعداد MailHog للتطوير المحلي
MailHog هو أسهل طريقة لإعداد اختبار البريد الإلكتروني محلياً. يشغّل خادم SMTP يلتقط جميع البريد الصادر ويعرضه في واجهة ويب.
التثبيت باستخدام Docker Compose
أضف MailHog إلى docker-compose.yml:
services:
mailhog:
image: mailhog/mailhog
ports:
- "1025:1025" # منفذ SMTP
- "8025:8025" # منفذ واجهة الويب
restart: unless-stopped
تهيئة تطبيقك لاستخدام MailHog
في ملف .env الخاص بالتطوير:
MAIL_HOST=localhost
MAIL_PORT=1025
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=null
جميع رسائل البريد الإلكتروني التي يرسلها تطبيقك في التطوير تذهب الآن إلى MailHog. ادخل إلى الصندوق على http://localhost:8025.
لماذا هذا أفضل من البريد الإلكتروني الحقيقي في التطوير
- رسائل البريد الإلكتروني لا تغادر جهازك أبداً — لا إرسال عرضي إلى مستخدمين حقيقيين
- تسليم فوري — لا انتظار لتسليم SMTP الحقيقي
- محتوى البريد الإلكتروني الكامل مرئي في المتصفح — رؤوس HTML وأجزاء النص والمرفقات
- لا حاجة للمصادقة
- سهل التفريغ بين تشغيلات الاختبار
اختبار البريد الإلكتروني الآلي باستخدام Mailinator API
للاختبارات من البداية إلى النهاية التي تحتاج إلى قراءة رسائل البريد الإلكتروني برمجياً، تُعدّ Mailinator API الخيار الأكثر مباشرة.
المستوى المجاني له حدود، لكن API المدفوعة الخاصة بهم ميسورة التكلفة لمعظم الفرق.
فحص أساسي للصندوق باستخدام fetch
async function getLatestEmail(inboxName) {
const response = await fetch(
`https://mailinator.com/api/v2/domains/mailinator.com/inboxes/${inboxName}`,
{
headers: {
Authorization: process.env.MAILINATOR_API_KEY
}
}
)
const data = await response.json()
if (!data.msgs || data.msgs.length === 0) {
throw new Error(`لم يتم العثور على رسائل بريد إلكتروني في الصندوق: ${inboxName}`)
}
return data.msgs[0] // أحدث بريد إلكتروني
}
async function getEmailBody(messageId) {
const response = await fetch(
`https://mailinator.com/api/v2/domains/mailinator.com/inboxes/test/messages/${messageId}`,
{
headers: {
Authorization: process.env.MAILINATOR_API_KEY
}
}
)
return response.json()
}
اختبار تسجيل كامل من البداية إلى النهاية باستخدام Playwright
import { test, expect } from '@playwright/test'
const TEST_EMAIL = `e2e-test-${Date.now()}@mailinator.com`
test('تسجيل المستخدم وتأكيد البريد الإلكتروني', async ({ page }) => {
// الخطوة 1: التسجيل في الموقع
await page.goto('https://staging.تطبيقك.com/register')
await page.fill('[name="email"]', TEST_EMAIL)
await page.fill('[name="password"]', 'TestPassword123!')
await page.click('[type="submit"]')
// توقع إعادة التوجيه إلى صفحة "تحقق من بريدك الإلكتروني"
await expect(page).toHaveURL(/check-email/)
// الخطوة 2: انتظار بريد التأكيد وجلبه
const email = await waitForEmail(TEST_EMAIL.split('@')[0], {
timeout: 30000,
subject: 'أكد بريدك الإلكتروني'
})
// الخطوة 3: استخراج رابط التأكيد من نص البريد الإلكتروني
const confirmUrl = extractConfirmationLink(email.body)
expect(confirmUrl).toBeTruthy()
// الخطوة 4: زيارة رابط التأكيد
await page.goto(confirmUrl)
// الخطوة 5: التحقق من نجاح التأكيد
await expect(page).toHaveURL(/dashboard/)
await expect(page.locator('h1')).toContainText('مرحباً')
})
async function waitForEmail(inbox, options = {}) {
const { timeout = 15000, subject } = options
const startTime = Date.now()
while (Date.now() - startTime < timeout) {
const response = await fetch(
`https://mailinator.com/api/v2/domains/mailinator.com/inboxes/${inbox}`,
{ headers: { Authorization: process.env.MAILINATOR_API_KEY } }
)
const data = await response.json()
const emails = data.msgs || []
const match = subject
? emails.find(e => e.subject?.includes(subject))
: emails[0]
if (match) {
// جلب نص البريد الإلكتروني الكامل
const bodyResponse = await fetch(
`https://mailinator.com/api/v2/domains/mailinator.com/inboxes/${inbox}/messages/${match.id}`,
{ headers: { Authorization: process.env.MAILINATOR_API_KEY } }
)
return bodyResponse.json()
}
await new Promise(resolve => setTimeout(resolve, 1000))
}
throw new Error(`لم يتم استقبال البريد الإلكتروني خلال ${timeout}ms`)
}
function extractConfirmationLink(emailBody) {
const htmlContent = emailBody.parts?.[0]?.body || ''
const urlPattern = /https?:\/\/[^\s"'<>]+confirm[^\s"'<>]*/i
const match = htmlContent.match(urlPattern)
return match?.[0] || null
}
استخدام عناوين فريدة لكل تشغيل اختبار
خطأ شائع هو إعادة استخدام نفس عنوان البريد الإلكتروني للاختبار عبر تشغيلات الاختبار. رسائل البريد القديمة تُلوث الصندوق وقد تلتقط الاختبارات البريد الإلكتروني الخاطئ.
النمط: عناوين فريدة مبنية على الطابع الزمني
// توليد صندوق وارد فريد لكل تشغيل اختبار
const testRunId = Date.now()
const email = `registration-test-${testRunId}@mailinator.com`
النمط: مبني على UUID لكل اختبار
import { randomUUID } from 'crypto'
function testEmail(prefix = 'test') {
return `${prefix}-${randomUUID().slice(0, 8)}@mailinator.com`
}
// الاستخدام
const email = testEmail('registration') // registration-a1b2c3d4@mailinator.com
هذا يضمن أن كل تشغيل اختبار لديه صندوق وارد جديد بدون تاريخ.
إعداد Mailpit (بديل MailHog الحديث)
Mailpit لديه واجهة أفضل وتطوير نشط وميزات أكثر من MailHog. إنه الخيار الأفضل للإعدادات الجديدة.
# docker-compose.yml
services:
mailpit:
image: axllent/mailpit
ports:
- "1025:1025" # SMTP
- "8025:8025" # واجهة الويب
environment:
MP_MAX_MESSAGES: 500
MP_DATABASE: /data/mailpit.db
MP_SMTP_AUTH_ACCEPT_ANY: 1
MP_SMTP_AUTH_ALLOW_INSECURE: 1
volumes:
- mailpit_data:/data
volumes:
mailpit_data:
يمتلك Mailpit أيضاً REST API للوصول البرمجي إلى صندوق الوارد، مما يجعله قابلاً للتطبيق للاختبارات الآلية في بيئة Docker Compose:
// Mailpit API — للاستخدام في إعدادات Docker Compose المحلية/CI
async function getMailpitInbox() {
const response = await fetch('http://localhost:8025/api/v1/messages')
return response.json()
}
async function deleteAllMessages() {
await fetch('http://localhost:8025/api/v1/messages', { method: 'DELETE' })
}
افرّغ الصندوق في بداية كل تشغيل اختبار باستخدام deleteAllMessages() لضمان حالة نظيفة.
منع الإرسال العرضي لرسائل الإنتاج
هذا أمر بالغ الأهمية. متغير بيئة مُهيَّأ بشكل خاطئ يُشير إلى بيانات اعتماد SMTP الخاصة بالإنتاج سيرسل رسائل بريد إلكتروني حقيقية إلى مستخدمين حقيقيين.
النهج 1: تبديل SMTP المستند إلى البيئة
// email.config.js
const emailConfig = {
development: {
host: 'localhost',
port: 1025,
secure: false,
auth: null
},
test: {
host: 'localhost',
port: 1025,
secure: false,
auth: null
},
staging: {
host: process.env.MAILHOG_HOST || 'mailhog',
port: 1025,
secure: false
},
production: {
host: process.env.SMTP_HOST,
port: 587,
secure: true,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS
}
}
}
export const smtpConfig = emailConfig[process.env.NODE_ENV] || emailConfig.development
النهج 2: قائمة بيضاء للنطاقات في البيئات غير الإنتاجية
function shouldSendEmail(toAddress) {
if (process.env.NODE_ENV === 'production') return true
const allowedTestDomains = [
'mailinator.com',
'guerrillamail.com',
'instanttempemail.com',
'شركتك.com' // عناوين اختبار داخلية
]
const domain = toAddress.split('@')[1]
return allowedTestDomains.includes(domain)
}
اختبار عرض البريد الإلكتروني عبر عملاء مختلفين
الأدوات المذكورة أعلاه تتحقق من تسليم البريد الإلكتروني ومحتواه، لكن ليس كيفية عرضه في عملاء مختلفين. HTML البريد الإلكتروني يتصرف بشكل مختلف عبر Gmail وOutlook وApple Mail والعملاء المحمولين.
لاختبارات العرض، استخدم Email on Acid أو Litmus — يُنشئان لقطات شاشة لبريدك الإلكتروني عبر أكثر من 90 عميلاً. كلاهما لديه إصدارات تجريبية مجانية.
للفحوصات الأبسط، تعرض ميزة معاينة البريد الإلكتروني في Mailtrap بريدك الإلكتروني في سياقات العملاء الشائعة.
الأسئلة الشائعة
هل يمكنني استخدام Gmail أو Outlook للاختبار الآلي؟ من الناحية التقنية نعم، باستخدام OAuth أو وصول IMAP. من الناحية العملية، إنه أبطأ وأكثر تعقيداً في الإعداد ويُقدّم حدوداً للمعدل وإدارة المصادقة. الأدوات المخصصة دائماً أسرع وأكثر موثوقية.
ما الفرق بين Mailtrap وMailHog؟ Mailtrap هي خدمة SaaS مُستضافة — رسائل بريدك الإلكتروني تُرسل إلى خوادمهم ويمكن عرضها في واجهة الويب الخاصة بهم. MailHog ذاتي الاستضافة — يعمل على جهازك أو في إعداد Docker Compose الخاص بك. MailHog لا يكلف شيئاً ولا تغادر البيانات بيئتك. Mailtrap أسهل في المشاركة عبر الفريق.
هل من الآمن استخدام Mailinator لبيانات الاختبار الحساسة؟ لا. صناديق وارد Mailinator عامة — أي شخص يعرف اسم الصندوق يمكنه قراءة رسائل البريد الإلكتروني. استخدم بيانات الاختبار فقط. لا تستخدم أبداً بيانات المستخدمين الحقيقيين في صناديق Mailinator. للاختبار الآلي الخاص، استخدم MailHog أو Mailpit في إعداد Docker Compose الخاص بك بدلاً من ذلك.
كيف أختبر تدفقات البريد الإلكتروني في خط أنابيب CI لـ GitHub Actions؟
أضف خدمة MailHog أو Mailpit إلى سير عمل GitHub Actions الخاص بك باستخدام مفتاح services في YAML سير العمل. يرسل تطبيقك إلى localhost:1025، وتقرأ اختباراتك عبر API المحلية.
jobs:
test:
services:
mailpit:
image: axllent/mailpit
ports:
- 1025:1025
- 8025:8025
هل يجب أن أختبر مع مزودي بريد إلكتروني حقيقيين في التجهيز؟ فقط إذا كنت بحاجة تحديداً إلى اختبار قابلية التسليم (تسجيل نقاط الرسائل غير المرغوب فيها، DKIM/DMARC، موضع صندوق الوارد). لاختبار وظائف تدفقات البريد الإلكتروني، أداة الاصطياد المحلية مثل MailHog أسرع وأكثر أماناً. قم بإجراء فحوصات قابلية التسليم الحقيقية بشكل منفصل، بتكرار أقل، باستخدام حسابات اختبار لدى كبار المزودين.