الأدلة التقنية

أدوات بيئة التطوير المحلية: ماذا تستخدم لكل خدمة؟

مقارنة بالقياس بين أدوات التقاط البريد والشهادات المحلية والأسماء ومحاكاة ال API والسحابة، وأيها تختار لمشروعك.

أدوات بيئة التطوير المحلية: ماذا تستخدم لكل خدمة؟

في الجزء الأول من السلسلة وصلنا إلى قاعدة بسيطة: الكود يبقى كما هو، والخدمة الخارجية يحل محلها على جهازك بديل يتحدث بروتوكولها نفسه، ورأينا أن المتصفح يعامل localhost كسياق آمن دون شهادة، وأن الأسماء الأخرى تحتاج DNS محلياً وHTTPS. والسؤال الآن عملي: أي أداة تستخدم لكل خدمة؟ فلكل خدمة ثلاث أدوات مشهورة أو أكثر، وبعضها توقف تطويره منذ سنوات، وبعضها تغيرت رخصته أو أصبح يحتاج حساباً، وأغلب المقالات التي تقارنها كتبت قبل هذه التغييرات.

والمواضيع في السلسلة:

وسوف نناقش في هذا المقال ما يلي:

  • صناديق التقاط البريد Mail Catchers: مقارنة بالقياس بين Mailpit وMailHog وsmtp4dev.
  • شهادات HTTPS المحلية: mkcert وجهة الإصدار الداخلية في Caddy وstep-ca، وما يفعله Traefik، مع مدة كل شهادة كما قسناها.
  • الأسماء المحلية: *.localhost وخدمات nip.io وsslip.io وملف hosts وdnsmasq وCoreDNS.
  • تخزين S3 المحلي ومحاكيات السحابة، وما تغير في LocalStack في 2026.
  • محاكاة ال API الخارجي: WireMock وMockoon وPrism.
  • استقبال ال Webhooks على جهازك: أنفاق cloudflared وngrok، وخدمة smee.io.
  • مكتبة Testcontainers ومواصفات Dev Containers، وأين يقف كل منهما من ملف Compose.
  • جدول قرار يجمع كل ذلك.

والصورة التالية تبين الخريطة التي يسير عليها المقال:

خريطة من ثماني بطاقات، كل بطاقة لخدمة خارجية وتحتها الأدوات المحلية التي تحاكيها: البريد مع Mailpit وsmtp4dev وMailHog بعلامة أنه متوقف، وHTTPS مع mkcert وCaddy وstep-ca، وDNS مع localhost وdnsmasq وCoreDNS وnip.io، وS3 مع MinIO وSeaweedFS وGarage، والسحابة مع LocalStack وMoto، وال API مع WireMock وMockoon وPrism، وال Webhooks مع cloudflared وngrok وsmee، والاعتماديات وبيئة العمل مع Testcontainers وDev Containers
لكل خدمة خارجية أكثر من أداة، والمقال يقارنها خدمة خدمة

صناديق التقاط البريد: Mailpit أم MailHog أم smtp4dev؟

شرحنا فكرة صندوق الالتقاط Mail Catcher في الجزء الأول، وهنا نقارن الأدوات الثلاث الأشهر بالأرقام. فقد شغلنا كل أداة في Container على شبكة Docker داخلية لا تصل إلى الإنترنت، وأرسلنا إلى كل منها 600 رسالة صغيرة على اتصال SMTP واحد بسكربت Python، ثم سألنا ال API عن عدد الرسائل المحفوظة، وقسنا الذاكرة بالأمر docker stats. والسكربت كما يلي:

# Sends N small messages to one SMTP mail catcher over a single connection and prints the time it took.
#   python send.py <host> <port> <count>
import smtplib, sys, time
from email.message import EmailMessage

host, port, count = sys.argv[1], int(sys.argv[2]), int(sys.argv[3])
start = time.perf_counter()
with smtplib.SMTP(host, port, timeout=60) as s:
    for i in range(count):
        m = EmailMessage()
        m["From"], m["To"], m["Subject"] = "[email protected]", f"user{i}@example.com", f"Order #{i} confirmed"
        m.set_content(f"Hello user{i}, your order #{i} is confirmed.\n")
        s.send_message(m)
print(f"{host}: {count} messages in {time.perf_counter() - start:.2f}s")
docker network create --internal lab-ldev-tools
docker run -d --name lab-ldev-mailpit2 --network lab-ldev-tools axllent/mailpit:v1.31.5
docker run -d --name lab-ldev-smtp4dev --network lab-ldev-tools rnwood/smtp4dev:3.15.0
docker run -d --name lab-ldev-mailhog  --network lab-ldev-tools mailhog/mailhog:v1.0.1

docker run --rm --name lab-ldev-sender --network lab-ldev-tools -v "$PWD:/lab:ro" python:3.13-alpine sh -c '
  python /lab/send.py lab-ldev-mailpit2 1025 600
  python /lab/send.py lab-ldev-mailhog 1025 600
  python /lab/send.py lab-ldev-smtp4dev 25 600
  wget -qO- "http://lab-ldev-mailpit2:8025/api/v1/messages?limit=1" | grep -o "\"total\":[0-9]*"
  wget -qO- "http://lab-ldev-mailhog:8025/api/v2/messages?limit=1" | grep -o "\"total\":[0-9]*"
  wget -qO- "http://lab-ldev-smtp4dev/api/messages?pageSize=1" | grep -o "\"rowCount\":[0-9]*"'

والمخرج سوف يكون كما يلي:

lab-ldev-mailpit2: 600 messages in 48.83s
lab-ldev-mailhog: 600 messages in 7.57s
lab-ldev-smtp4dev: 600 messages in 178.11s
"total":500
"total":600
"rowCount":100

والجدول التالي يجمع المخرج مع الذاكرة وحجم ال Image وحالة كل مشروع كما قرأناها على GitHub في 12 أكتوبر 2026:

المقياسMailpitsmtp4devMailHog
الإصدار المختبرالإصدار v1.31.5 في 11 أكتوبر 2026الإصدار 3.15.0 في مارس 2026الإصدار v1.0.1 في أغسطس 2020
الرخصةMITBSD-3-ClauseMIT
حالة التطويرنشطنشطلا يوجد إصدار منذ 2020، وآخر تعديل في المستودع في فبراير 2024
حجم ال Image كما يعرضه docker images59.9MB588MB572MB
الذاكرة قبل الإرسال13MiB76MiB4.8MiB
الذاكرة بعد 600 رسالة22MiB177MiB4.8MiB
الرسائل المحفوظة من 600500100600
زمن إرسال 600 رسالة (للتوضيح فقط)48.8 ثانية178.1 ثانية7.6 ثانية

في النتائج أعلاه لاحظ التالي:

  • الأزمنة قيست على جهاز واحد كانت تعمل عليه في الوقت نفسه أعمال أخرى ثقيلة، فخذها للمقارنة بين الأدوات في هذه التجربة فقط، وليس كأداء متوقع على جهازك، والمهم منها أن الثلاث تستقبل مئات الرسائل في دقائق على الأكثر، وهذا أكثر مما يحتاجه أي اختبار.
  • العدد المحفوظ هو أهم سطر في الجدول، فكل أداة تحذف القديم بحد افتراضي مختلف: Mailpit يحتفظ بآخر 500 رسالة بحسب الخيار --max في خيارات التشغيل، وsmtp4dev يحتفظ بآخر 100 بحسب NumberOfMessagesToKeep في ملف إعداده، وMailHog يحفظ كل شيء في الذاكرة حتى يتوقف. وهذا يعني أن اختبار حمل يرسل ألف رسالة ثم يبحث عن الأولى سوف يفشل في الأداتين الأوليين دون أي خطأ في تطبيقك.
  • أداة Mailpit تحفظ الرسائل افتراضياً في ملف مؤقت يحذف عندما يتوقف، فإذا أردت أن تبقى الرسائل بعد إعادة التشغيل فحدد له قاعدة بيانات بالخيار --database في Volume.

أداة Mailpit

أداة Mailpit برنامج واحد مكتوب بلغة Go، فيه سيرفر SMTP على المنفذ 1025 وواجهة ويب وAPI على المنفذ 8025، وهو الذي نستخدمه في تجارب هذا الموقع.

نقاط القوة: API كامل للبحث والقراءة والحذف تستخدمه الاختبارات الآلية، وميزة Chaos التي أضيفت في الإصدار 1.20.0 وتجعل سيرفر SMTP يرفض الرسائل برمز خطأ تختاره عند المرسل أو المستلم أو المصادقة لتختبر كيف يتصرف تطبيقك عندما يفشل البريد كما في توثيق Chaos، وإطلاق الرسالة Release إلى سيرفر حقيقي يدوياً أو تلقائياً مع قائمة عناوين محظورة كما في توثيق SMTP Relay، وفحص الروابط وتوافق HTML مع برامج البريد، وWebhook عند وصول رسالة، وصغر حجمه.

نقاط الضعف: الحد الافتراضي 500 رسالة، والتخزين المؤقت الذي يضيع عند إعادة التشغيل، وميزة Relay نفسها، فهي الطريق الوحيد الذي يخرج منه بريد حقيقي من بيئة التطوير، فلا تفعلها إلا لعناوين تملكها.

أداة smtp4dev

أداة smtp4dev مكتوبة ب .NET، وبدأت كأداة لنظام Windows ثم أصبحت تعمل في Docker على كل الأنظمة.

نقاط القوة: يقدم IMAP وPOP3 بجانب SMTP، فتستطيع أن تفتح الرسائل من برنامج بريد حقيقي مثل Thunderbird، ويدعم TLS والمصادقة في SMTP لتختبر إعداد الإنتاج نفسه، ويعرض تحذيرات عن محتوى الرسالة، ويحفظ الرسائل في قاعدة SQLite.

نقاط الضعف: أكبر الثلاثة في الحجم والذاكرة، وكان الأبطأ في تجربتنا، والحد الافتراضي 100 رسالة فقط.

أداة MailHog القديمة

أداة MailHog كانت الأداة الأشهر لسنوات، وتجدها في ملفات Compose ودروس كثيرة، وفيها ميزة اسمها Jim تقطع الاتصال وترفض الرسائل عشوائياً لاختبار الأعطال.

نقاط القوة: أقل الثلاثة استهلاكاً للذاكرة في تجربتنا، وأسرعها في الاستقبال، ولا يحذف الرسائل إلا عندما يتوقف.

نقاط الضعف: لم يصدر منه إصدار منذ أغسطس 2020، وأي مشكلة أمنية أو توافق مع مكتبات اليوم لن تصلح، والتخزين في الذاكرة يعني أن الرسائل تضيع مع إعادة التشغيل، وال API أبسط من API Mailpit. ولهذا لا ننصح باستخدامه في مشروع جديد، وإذا كان في ملفات مشروعك فاستبدال ال Image ب Mailpit يكفي غالباً لأن الاثنين يستخدمان المنفذين 1025 و8025.

شهادات HTTPS المحلية: من يصدرها، ولكم من الوقت؟

عندما تحتاج HTTPS على اسم ليس localhost تحتاج جهة إصدار محلية Local CA تصدر الشهادة، ثم تجعل المتصفح والأدوات تثق بها، وقد شرحنا الشهادات وسلسلة الثقة في مقال الشهادات الرقمية. والأدوات هنا تختلف في سؤالين: كم تعيش الشهادة، وماذا تفعل الأداة بمخزن الثقة Trust Store على جهازك؟ وقد أصدرنا من كل أداة شهادة في Container مؤقت وقرأنا تواريخها بالأمر openssl x509 أو step certificate inspect، والجدول التالي يبين النتائج:

الأداةالشهادة الرئيسية Rootالشهادة الوسيطةشهادة الموقعمخزن الثقة
mkcert v1.4.410 سنواتلا توجدسنتان وثلاثة أشهرالأمر mkcert -install يضيفها إلى مخزن النظام وFirefox وJava
Caddy 2.11.7 مع tls internalقرابة 10 سنوات7 أيام12 ساعةيحاول إضافتها إلى مخزن النظام تلقائياً إلا إذا كتبت skip_install_trust
step-ca 0.30.210 سنوات10 سنوات24 ساعةلا يلمس جهازك، وأنت تقرر أين تضيفها

أداة mkcert

أداة mkcert برنامج سطر أوامر واحد ينشئ جهة إصدار في مجلد على جهازك، ويصدر منها شهادة لأي أسماء تكتبها، ولا يشغل أي سيرفر. والمثال التالي يصدر شهادة لنطاق lab.test وكل ما تحته داخل Container مؤقت، فلا يدخل شيء إلى جهازك:

docker run --rm -e CAROOT=/ca -v "$PWD/ca:/ca" -v "$PWD/certs:/certs" -w /certs alpine:3.22 sh -c '
  wget -q -O /usr/local/bin/mkcert https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/mkcert-v1.4.4-linux-amd64
  chmod +x /usr/local/bin/mkcert
  mkcert -cert-file lab.pem -key-file lab-key.pem "lab.test" "*.lab.test"'

والمخرج سوف يكون كما يلي:

Created a new local CA 💥
Note: the local CA is not installed in the system trust store.
Run "mkcert -install" for certificates to be trusted automatically ⚠️

Created a new certificate valid for the following names 📜
 - "lab.test"
 - "*.lab.test"

Reminder: X.509 wildcards only go one level deep, so this won't match a.b.lab.test ℹ️

The certificate is at "lab.pem" and the key at "lab-key.pem" ✅

It will expire on 12 January 2029 🗓

في المخرج أعلاه لاحظ التالي:

  • لم نشغل mkcert -install، فالشهادة صدرت لكن لا أحد يثق بها بعد، وفي الجزء الثالث نجعل متصفحاً داخل Container وحده يثق بها، دون أن نلمس جهازك.
  • التذكير بأن الشهادة الشاملة Wildcard تغطي مستوى واحداً فقط، فـ *.lab.test لا تغطي api.v2.lab.test، وسوف نرى الخطأ بعينه في الجزء الثالث.
  • عندما قرأنا الشهادة الرئيسية بالأمر openssl x509 -ext basicConstraints,nameConstraints وجدنا CA:TRUE, pathlen:0 ولم نجد أي قيود على الأسماء Name Constraints، وهذا يعني أن هذه الجهة تستطيع أن تصدر شهادة يقبلها جهازك لأي موقع في العالم، ولهذا يحذر التوثيق من مشاركة الملف rootCA-key.pem.

نقاط القوة: أبسط الأدوات، وأمر واحد يصدر الشهادة، ويعمل على Windows وmacOS وLinux، ويضيف الجهة إلى Firefox وJava بجانب النظام.

نقاط الضعف: آخر إصدار v1.4.4 في أبريل 2022 وآخر تعديل في المستودع في أغسطس 2024، والأداة تعمل ولا تحتاج تغييرات كثيرة لكن لا تتوقع ميزات جديدة، والشهادة تعيش أكثر من سنتين، وليس لديها ACME، فتنقل الملفات إلى ال Reverse Proxy بنفسك.

جهة الإصدار الداخلية في Caddy

سيرفر Caddy الذي شرحنا تثبيته في دليل Caddy يحمل جهة إصدار داخلية، فإذا كان اسم الموقع localhost أو ينتهي بـ .localhost أو .local أو .internal أو .home.arpa أو كان عنوان IP، يصدر له شهادة منها تلقائياً كما يشرح توثيق HTTPS التلقائي، ولأي اسم آخر تكتب tls internal. والإعداد الذي استخدمناه في التجربة:

{
	skip_install_trust
}
app.localhost {
	tls internal
	respond "hi"
}

وعند التشغيل كتب Caddy في السجل:

"logger":"pki.ca.local","msg":"root certificate trust store installation disabled; unconfigured clients may show warnings","path":"storage:pki/authorities/local/root.crt"

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

نقاط القوة: لا توجد أداة إضافية، والشهادات تتجدد وحدها، وعمر الشهادة 12 ساعة يقلل أثر تسرب مفتاح الموقع.

نقاط الضعف: الشهادة داخل Caddy فقط، فالخدمات التي تحتاج ملف شهادة لها لا تجده بسهولة، والثقة بالجهة في المتصفحات والأدوات الأخرى عمل يدوي عندما يعمل Caddy في Container.

جهة الإصدار step-ca للفريق

مشروع step-ca جهة إصدار كاملة برخصة Apache-2.0 تعمل كسيرفر، وتصدر الشهادات عبر عدة طرق Provisioners منها ACME، أي البروتوكول نفسه الذي تستخدمه Let's Encrypt، وبالتالي يستطيع Caddy أو Traefik أو certbot أن يطلبوا منها الشهادات كما يطلبونها في الإنتاج. وقد أصدرنا منها شهادة لاسم app.lab.test، والمخرج كما يلي:

X.509v3 TLS Certificate (ECDSA P-256) [Serial: 6991...9916]
  Subject:     app.lab.test
  Issuer:      lab-ldev Intermediate CA
  Provisioner: admin [ID: 5eup...61j4]
  Valid from:  2026-10-12T11:34:45Z
          to:  2026-10-13T11:35:45Z

نقاط القوة: أقرب الأدوات لإعداد الإنتاج، وتصلح جهة إصدار للفريق أو للشبكة الداخلية، وليس لجهاز واحد فقط، وتدعم شهادات SSH كما ذكرنا في مقال الهوية والوصول المميز.

نقاط الضعف: سيرفر يحتاج إعداداً وكلمات مرور ونسخاً احتياطياً، وهذا أكثر مما يحتاجه مطور واحد على جهازه.

وماذا عن Traefik؟

أما Traefik الذي شرحناه في دليل Traefik لا يحمل جهة إصدار محلية، فإذا لم تعطه شهادة يستخدم شهادة يولدها بنفسه ولا يثق بها أي متصفح كما يذكر توثيق شهادات TLS، والحل أن تعطيه ملفات mkcert في قسم tls.certificates، أو أن تجعله يطلب الشهادات من step-ca عبر ACME.

الأسماء المحلية: من يجيب عن app.lab.test؟

رأينا في الجزء الأول أن *.localhost يعمل في المتصفح وcurl ولا يعمل في كل برنامج، والخيارات الأخرى خمسة:

  1. خدمات nip.io وsslip.io: نطاقات عامة تجيب عن أي اسم فيه عنوان IP بهذا العنوان نفسه، فالاسم app.127.0.0.1.nip.io يشير إلى 127.0.0.1، والاسم app.192-168-1-20.sslip.io يشير إلى 192.168.1.20، وقد تحققنا من ذلك بالأمر nslookup. وهي مفيدة لتجربة سريعة من الهاتف دون أي إعداد، لكنها تحتاج الإنترنت، وبعض الراوترات وسيرفرات DNS ترفض الإجابات التي تحمل عناوين خاصة لتمنع هجوم DNS Rebinding، كما يفعل الخيار --stop-dns-rebind في dnsmasq، فلا تعتمد عليها في اختبارات آلية. ورخصة sslip.io هي Apache-2.0 وتستطيع تشغيل سيرفرها بنفسك.
  2. سيرفر dnsmasq: سيرفر DNS صغير برخصة GPL، وسطر واحد مثل address=/lab.test/127.0.0.1 يجعل كل اسم تحت lab.test يشير إلى جهازك، وهو الخيار المعتاد على أجهزة Linux وmacOS، وقد استخدمناه في مقال DDNS ومختبرات الشبكات.
  3. سيرفر CoreDNS: سيرفر DNS برخصة Apache-2.0 وهو المستخدم داخل Kubernetes، وفيه إضافة template التي تجيب عن كل اسم في نطاق بعنوان واحد، وهو مناسب داخل ملف Compose لأن ال Containers تستخدمه مباشرة، وهذا ما سوف نفعله في الجزء الثالث.
  4. نطاق حقيقي تملكه: سجل *.dev.example-company.com في DNS العام يشير إلى 127.0.0.1 يعمل في كل مكان، وتستطيع أن تصدر له شهادة حقيقية بالتحقق عبر DNS، لكنه ينشر أسماء بيئتك الداخلية للعالم، ويتأثر بنفس مشكلة DNS Rebinding.

ملف hosts: يربط اسماً كاملاً بعنوان، ولا يعرف النجمة، وقد اختبرنا ذلك في Container مؤقت دون شبكة بإضافة سطرين إلى /etc/hosts فيه:

docker run --rm --name lab-ldev-hosts --network none lab-ldev-pw:1 sh -c '
  printf "127.0.0.1 *.lab.test\n127.0.0.1 app.lab.test\n" >> /etc/hosts; tail -2 /etc/hosts
  for n in app.lab.test api.lab.test "*.lab.test"; do printf "%s -> " "$n"; getent hosts "$n" || echo "not found"; done'

والمخرج سوف يكون كما يلي:

127.0.0.1 *.lab.test
127.0.0.1 app.lab.test
app.lab.test -> 127.0.0.1       app.lab.test
api.lab.test -> not found
*.lab.test -> 127.0.0.1       *.lab.test

لاحظ أن api.lab.test لم يجده النظام، وأن السطر الأول لم يصبح قاعدة لكل الأسماء، وإنما أصبح اسماً غريباً حرفه الأول نجمة كما تصف صفحة hosts شكل السطر: عنوان ثم أسماء. وتعديل ملف hosts على جهازك يحتاج صلاحيات المدير، ويؤثر على كل البرامج، ولهذا لا نعدله في تجارب هذه السلسلة.

تخزين S3 ومحاكيات السحابة

لتخزين الملفات تشغل على جهازك سيرفراً يتحدث واجهة S3، وقد قارنا Garage وSeaweedFS وMinIO بالتفصيل في دليل S3 على سيرفرك ومقال أي تخزين S3 يناسبك الذي يشرح ما تغير في MinIO، وطريقة الاستخدام من الكود في مقال S3 من داخل تطبيقك، فلن نكرر ذلك هنا.

أما إذا كان تطبيقك يستخدم خدمات AWS أخرى مثل SQS وSNS وLambda وDynamoDB، فالأداة الأشهر كانت LocalStack، وقد تغير وضعها في 2026: فبحسب إعلان الشركة في 5 مارس 2026 أصبحت LocalStack for AWS تصدر منذ 23 مارس 2026 كـ Docker Image واحدة تحتاج رمز مصادقة Auth Token، أي تحتاج حساباً، وأصبح المستودع المفتوح على GitHub مؤرشفاً للقراءة فقط، والخطة المجانية Hobby في صفحة الأسعار مكتوب عليها أنها للهواة والاستخدام غير التجاري. وقد شغلنا localstack/localstack:latest دون رمز لنرى ذلك، والمخرج كما يلي:

LocalStack version: 2026.9.1
LocalStack build date: 2026-10-07

Localstack returning with exit code 55. Reason:
===============================================
License activation failed! 🔑❌

Reason: No credentials were found in the environment. Please set the LOCALSTACK_AUTH_TOKEN variable to a valid auth token.

وهذا يعني أن فريقاً في شركة يحتاج خطة مدفوعة، أو أن يثبت إصداراً قديماً من قبل التغيير ويقبل أنه لن يحصل على إصلاحات، أو أن ينتقل إلى Moto برخصة Apache-2.0، وهي مكتبة Python تحاكي واجهات AWS ولها وضع سيرفر Server Mode تتصل به من أي لغة، وتغطيتها تختلف من خدمة إلى أخرى فراجعها قبل الاعتماد عليها. ورأينا أن الأفضل لأغلب الفرق أن تقلل الاعتماد على المحاكي أصلاً: S3 عبر سيرفر S3 حقيقي، والطوابير عبر طابور تشغله أنت في الاختبار، واختبار نهائي واحد على حساب AWS للاختبار.

محاكاة ال API الخارجي

عندما يعتمد تطبيقك على API خارجي، مثل بوابة دفع أو مزود رسائل SMS أو خدمة شحن، تحتاج سيرفراً يرد على طلباتك كما يرد ذلك ال API، ويعطيك الردود الصعبة التي لا يعطيها ال API الحقيقي بسهولة: الخطأ 500، والتأخير 30 ثانية، والرد المكرر. والأدوات الثلاث الأشهر:

  • أداة WireMock برخصة Apache-2.0، مكتوب ب Java ويعمل كمكتبة داخل الاختبار أو كسيرفر في Container، ويسجل الطلبات والردود من ال API الحقيقي ثم يعيدها كما في توثيق التسجيل والإعادة، ويحاكي التأخير والأعطال. نقاط القوة: الأقدم والأغنى بالميزات ويوجد له مكتبات لعدة لغات. نقاط الضعف: الإعداد بملفات JSON يطول مع ال APIs الكبيرة.
  • أداة Mockoon برخصة MIT، تطبيق سطح مكتب تصمم فيه الردود بواجهة رسومية، ثم تشغلها في CI بأداة Mockoon CLI. نقاط القوة: أسهل بداية لمن لا يحب ملفات الإعداد. نقاط الضعف: العمل المشترك للفرق وتشغيل المحاكاة كخدمة في نسخة Mockoon Pro المدفوعة، من 10 يورو للرخصة شهرياً بحد أدنى 5 رخص كما في صفحة الأسعار في 12 أكتوبر 2026.
  • أداة Prism برخصة Apache-2.0، يقرأ ملف OpenAPI ويولد منه سيرفراً يرد بأمثلة الملف، ويعمل أيضاً كـ Proxy يتحقق من أن طلباتك والردود الحقيقية تطابق الملف. نقاط القوة: لا تكتب أي رد بيدك إذا كان لدى المزود ملف OpenAPI. نقاط الضعف: الردود ثابتة من الأمثلة ولا تحمل منطقاً.

وقبل أن تختار أداة تذكر أن المحاكي يرد بما تتوقعه أنت، وليس بما يفعله المزود فعلاً، ولهذا يأتي معه اختبار العقد Contract Test الذي شرحناه في مقال أنواع الاختبارات.

استقبال ال Webhooks على جهازك

ال Webhook هو الطلب الذي يرسله المزود إلى تطبيقك عندما يحدث شيء، مثل نجاح الدفع أو دفع كود إلى GitHub، والمشكلة أن المزود على الإنترنت وتطبيقك على localhost. والحل نفق Tunnel يخرج من جهازك إلى خدمة عامة تعطيك رابطاً، فيصل الطلب إلى الرابط ثم يعود عبر النفق إلى تطبيقك، كما تبين الصورة التالية:

أربع خطوات مرقمة: المزود مثل بوابة الدفع أو GitHub يرسل طلب POST إلى رابط عام، والرابط عند خدمة النفق مثل trycloudflare أو ngrok أو smee.io، والخدمة ترسل الطلب عبر اتصال خرج من قبل من جهاز المطور، وعلى الجهاز برنامج النفق cloudflared أو ngrok أو smee-client يسلم الطلب إلى التطبيق على localhost:3000، وفي الأسفل تحذير أن الرابط مفتوح لكل الإنترنت ما دام النفق يعمل
النفق يخرج من جهازك، فلا تفتح منفذاً، لكن الرابط العام يصل إليه أي أحد

وفكرة الأنفاق العكسية Reverse Tunnels شرحناها في مقال الأنفاق، وهنا ما يخص التطوير، والحدود كما قرأناها في 12 أكتوبر 2026:

  • أنفاق Quick Tunnels من Cloudflare: الأمر cloudflared tunnel --url http://localhost:3000 يعطيك رابطاً على trycloudflare.com دون حساب، والبرنامج cloudflared برخصة Apache-2.0، وبحسب التوثيق يقبل النفق 200 طلب في الوقت نفسه ويرد بالخطأ 429 بعدها، ولا يدعم Server-Sent Events، والرابط يتغير مع كل تشغيل، وهو للتطوير والاختبار فقط.
  • خدمة ngrok: خدمة تجارية تحتاج حساباً، والخطة المجانية في صفحة الأسعار تعطي نطاق تطوير واحداً ثابتاً، و1GB من نقل البيانات، و20 ألف طلب HTTP، وثلاث نقاط اتصال Endpoints، وتعرض صفحة تحذير للزائر قبل طلبات المتصفح، وفيها أداة لفحص الطلبات وإعادة إرسالها مفيدة جداً مع ال Webhooks.
  • خدمة smee.io: خدمة من مشروع Probot برخصة MIT، لا تفتح نفقاً، وإنما تستقبل ال Webhook على رابط قناة وترسله عبر Server-Sent Events إلى البرنامج smee-client على جهازك الذي يعيد إرساله إلى تطبيقك، ويقول المستودع صراحة إنها للتطوير وليست للإنتاج، ولاحظ أن من يعرف رابط القناة يرى ما يصل إليها.

وفي كل هذه الطرق تذكر أن الرابط العام يصل إلى تطبيقك وهو يعمل بإعداد التطوير، فتحقق من توقيع ال Webhook Signature في كودك كما في الإنتاج، وأغلق النفق عندما تنتهي، وإذا كان المزود يقدم أداة سطر أوامر ترسل أحداثاً تجريبية إلى localhost مباشرة فهي أفضل من النفق لأنها لا تنشر شيئاً.

مكتبة Testcontainers والاعتماديات داخل الاختبار نفسه

مكتبة Testcontainers مفتوحة المصدر برخصة MIT، لها نسخ ل Java وGo و.NET وNode.js وPython وRust وغيرها، وفكرتها أن الاختبار نفسه ينشئ Container لقاعدة البيانات أو للطابور أو لصندوق البريد قبل أن يبدأ، ويحذفه بعد أن ينتهي، وقد ذكرناها في قسم اختبار التكامل. والفرق بينها وبين ملف Compose في الغرض: ملف Compose يشغل بيئة تبقى تعمل وأنت تطور وتفتح التطبيق في المتصفح، وTestcontainers يشغل لكل مجموعة اختبارات نسخة نظيفة معزولة تختفي بعدها، فلا تتأثر الاختبارات ببيانات تركتها أنت أو تركها اختبار آخر. وأغلب الفرق تحتاج الاثنين: Compose للتطوير اليومي، وTestcontainers لاختبارات التكامل في CI.

مواصفات Dev Containers لبيئة عمل موحدة

كل ما سبق يشغل الخدمات حول تطبيقك، أما Dev Containers فيشغل بيئة التطوير نفسها، أي اللغة والأدوات والإضافات، داخل Container يصفه ملف devcontainer.json في المستودع، فيفتح كل مطور المشروع بالأدوات نفسها وبالإصدارات نفسها. والمواصفات مفتوحة برخصة CC-BY-4.0، وأداة سطر الأوامر برخصة MIT، وتدعمها بحسب قائمة الأدوات الداعمة بيئات مثل Visual Studio Code وVisual Studio وIntelliJ IDEA وخدمات مثل GitHub Codespaces وDevPod. وتستطيع أن تشير من devcontainer.json إلى ملف Compose، فتعمل بيئة العمل والخدمات المحلية معاً بأمر واحد، وإذا أردت بيئة تطوير في المتصفح لفريقك على سيرفرك فهذا موضوع مقال قادم بإذن الله في مسار منصات المطورين.

جدول القرار

الحاجةالخيار الأولمتى تختار غيره
التقاط البريدMailpitأداة smtp4dev إذا احتجت IMAP أو POP3 لتفتح الرسائل من برنامج بريد، وتجنب MailHog في المشاريع الجديدة
شهادات HTTPS لمطور واحدmkcert، أو tls internal إذا كان Caddy أمام التطبيقلا تحتاج شيئاً إذا كان كل شيء على localhost في متصفح جهازك
شهادات HTTPS لفريق أو شبكة داخليةجهة الإصدار step-ca عبر ACMEشهادة حقيقية بالتحقق عبر DNS إذا كان لديك نطاق
أسماء للمتصفح فقط*.localhostعندما تتصل الخدمات ببعضها بالأسماء
أسماء للمتصفح والخدمات معاًCoreDNS أو dnsmasq لنطاق .testخدمة nip.io لتجربة سريعة من الهاتف
تخزين S3Garage أو SeaweedFS أو MinIO بحسب المقارنةحساب S3 للاختبار عند المزود قبل الإصدار
خدمات AWS الأخرىمكتبة Moto، أو LocalStack بخطة تناسب استخدامكحساب AWS للاختبار عندما تهمك الصلاحيات والحدود الحقيقية
واجهة API خارجيةWireMockأداة Prism إذا كان لدى المزود ملف OpenAPI، وMockoon إذا كنت تفضل الواجهة الرسومية
استقبال Webhooks من مزود خارجيأداة المزود نفسه إن وجدت، ثم Quick Tunnel من Cloudflareخدمة ngrok إذا احتجت رابطاً ثابتاً وفحص الطلبات
الاعتماديات في الاختبارTestcontainersخدمات Compose في CI إذا كان المشروع صغيراً
توحيد بيئة الفريقDev Containersملف README بإصدارات ثابتة إذا كان الفريق شخصين

أخطاء شائعة

  • البحث عن رسالة قديمة في صندوق الالتقاط: Mailpit يحتفظ بآخر 500 وsmtp4dev بآخر 100، فاحذف الرسائل في بداية كل اختبار وابحث عن رسالة الاختبار نفسه.
  • تفعيل Relay في Mailpit لكل العناوين: هذا يعيد مشكلة البريد الحقيقي من بيئة التطوير، فإن احتجته فقيده بعناوين تملكها.
  • تشغيل mkcert -install أو Caddy دون skip_install_trust دون أن تفكر: كل منهما يضيف جهة إصدار إلى مخزن جهازك، ومفتاحها على قرصك.
  • كتابة *.lab.test في ملف hosts: ملف hosts لا يعرف النجمة، كما رأينا في التجربة.
  • الاعتماد على localstack/localstack:latest في CI دون رمز: منذ مارس 2026 يتوقف ال Container بالرمز 55، ويفشل خط CI كله.
  • نفق مفتوح طوال اليوم: الرابط العام يصل إلى تطبيقك بإعداد التطوير، ومعه صفحات الأخطاء المفصلة.

الخلاصة

  • أداة Mailpit هي الخيار الأول لالتقاط البريد بفضل ال API وميزة Chaos وحجمه، وsmtp4dev عندما تحتاج IMAP، وMailHog متوقف منذ 2020.
  • في تجربتنا حفظ Mailpit آخر 500 رسالة من 600، وsmtp4dev آخر 100، وMailHog الستمائة كلها في الذاكرة.
  • شهادة mkcert تعيش سنتين وثلاثة أشهر، وشهادة Caddy الداخلية 12 ساعة، وشهادة step-ca الافتراضية 24 ساعة، وجهة mkcert تستطيع أن توقع لأي اسم.
  • ملف hosts لا يفهم النجمة، و*.localhost للمتصفح، وCoreDNS أو dnsmasq لنطاق .test عندما تحتاج الأسماء في كل مكان.
  • محاكي LocalStack أصبح يحتاج رمز مصادقة منذ 23 مارس 2026، وMoto بديل مفتوح المصدر.
  • أدوات WireMock وMockoon وPrism لمحاكاة ال API، وQuick Tunnel من Cloudflare أو ngrok أو smee.io لاستقبال ال Webhooks مع إغلاقها بعد الاستخدام.
  • مكتبة Testcontainers للاعتماديات داخل الاختبار، وDev Containers لبيئة العمل نفسها، وملف Compose للبيئة التي تطور عليها كل يوم.

وفي الجزء الثالث بإذن الله نجمع Mailpit وmkcert وCaddy وCoreDNS مع تطبيق صغير في ملف Docker Compose واحد، ونكتب اختباراً آلياً يتأكد أن بريد استعادة كلمة المرور يصل، ثم نكسر البيئة بأربع طرق.

دليلبيئة التطوير المحلية: كيف تحاكي البريد وDNS وHTTPS على جهازكلماذا تستبدل البريد وDNS وHTTPS ببدائل محلية أثناء التطوير، وكيف يعامل المتصفح localhost، وأي النطاقات تتجنب.دليلبيئة تطوير محلية كاملة بملف Docker Compose واحدنبني بيئة تطوير فيها البريد وHTTPS والأسماء المحلية، ونكتب اختباراً آلياً لبريد استعادة كلمة المرور ثم نكسرها.دليلأي تخزين S3 مستضاف ذاتياً يناسبك؟ وما الذي تغير في MinIOمقارنة عملية بين أشهر أدوات تخزين S3 المستضافة ذاتياً بقياسات على جهاز واحد، لتعرف أيها يناسب سيرفرك وماذا حدث فعلاً ل MinIO.دليلما هو النفق Tunnel في الشبكات؟ ومتى تحتاج إليهكيف تصل إلى قاعدة بيانات لا تراها من الإنترنت، وتنشر خدمة من خلف CGNAT دون فتح منافذ، ومتى تحتاج إلى VPN بدل النفق.

سجل التحديثات

  • أكتوبر 2026: كتابة المقال، والقياسات على Mailpit v1.31.5 وsmtp4dev 3.15.0 وMailHog v1.0.1 مع Python 3.13 على Docker Engine 29.8.0، والشهادات من mkcert v1.4.4 وCaddy 2.11.7 وstep-ca 0.30.2، وتشغيل localstack/localstack:latest (الإصدار 2026.9.1)، والرخص وحالة المشاريع من GitHub، وحدود ngrok وCloudflare Quick Tunnels وخطط LocalStack كما قرأناها في 12 أكتوبر 2026.
Could not load content
نشرة عرب رووت | ArabRoot

معرفة تستحق مكاناً في بريدك.

مقالات مختارة وأدوات مفيدة وأفكار لمشروعك القادم، في رسالة واحدة كل أسبوع.

يمكنك إلغاء الاشتراك متى شئت. الخصوصية

تم استلام طلبك. افتح بريدك واضغط رابط التأكيد لإتمام الاشتراك.