اختبار الروابط العميقة
الرابط العميق (Deep Link) رابط يفتح شاشة محددة داخل تطبيق الجوال بدلاً من شاشته الأولى. فالنقر على https://shop.example.com/orders/A-1042 في رسالة شحن بالبريد الإلكتروني يجب أن يعرض الطلب A-1042 في تطبيق المتجر. وتصل الروابط إلى المستخدمين عبر البريد الإلكتروني والرسائل النصية ورموز QR والإشعارات الفورية والتطبيقات الأخرى، وكل رابط منها نقطة دخول تتجاوز التنقل المعتاد في التطبيق.
واختبار الروابط العميقة يتحقق من أن كل نقطة دخول كهذه توصل إلى الشاشة الصحيحة بالبيانات الصحيحة في كل حالة قد يكون فيها المستخدم: التطبيق مثبّت أو غير مثبّت، يعمل أو مغلق، والمستخدم مسجّل الدخول أو غير مسجّل، أو مسجّل الدخول بحساب شخص آخر. وهو يُبنى على سيناريوهات الجوال التي تعرفها، ويضيف قواعد المنصة التي تقرر هل يفتح الرابط التطبيق أصلاً.
لماذا يهم
تحمل الروابط العميقة اللحظات المهمة: حملة تسويقية، وإعادة تعيين كلمة المرور، وتحديث حالة التوصيل. وحين يتعطل أحدها يكون الإخفاق صامتاً: يصل المستخدم إلى الشاشة الرئيسية، أو إلى صفحة ويب تقول «غير موجودة»، أو الأسوأ إلى بيانات شخص آخر، ويعرف الفريق بذلك من تذاكر الدعم. وكثير من الأسباب يقع خارج شيفرة التطبيق: في ملف على الموقع الإلكتروني، أو شهادة توقيع، أو إعادة توجيه أضافتها أداة بريد، لذا لا تكشفها اختبارات واجهة المستخدم المعتادة التي تبدأ من داخل التطبيق.
والرابط العميق أيضاً مُدخل يأتي من خارج التطبيق. يستطيع أي أحد صياغته، فيجب أن يتحقق التطبيق من صحته، وألا يتجاوز تسجيل الدخول أو ينفّذ إجراءً دون تأكيد لمجرد أن رابطاً طلب ذلك. وهو بالنسبة للمختبِر مجال مركّز عالي القيمة: مجموعة صغيرة مختارة جيداً من التوليفات تغطي المخاطر الحقيقية متى عرفت أيّ الحالات وقواعد المنصة تجمع بينها.
المخططات المخصصة وAndroid App Links وUniversal Links في iOS
رابط مخطط URL المخصص (Custom URL scheme) يستخدم مخططاً يسجّله التطبيق لنفسه، مثل shop://orders/A-1042. وهو بسيط، لكن لا شيء يثبت أن المخطط ملك لتطبيقك: يستطيع تطبيق آخر تسجيل المخطط نفسه، وإذا لم يكن هناك تطبيق مثبّت يعالجه يفشل الرابط ببساطة. احتفظ به للروابط داخل منتجاتك، لا للروابط التي ترسلها إلى المستخدمين.
أما Android App Links فهي روابط https عادية يتحقق منها Android. يعلن التطبيق النطاق في مرشّح نوايا (Intent filter)، أي المدخل في ملف Manifest الذي يسرد الروابط التي يستطيع التطبيق فتحها، مع android:autoVerify="true"، وينشر الموقع الملف /.well-known/assetlinks.json وفيه اسم حزمة التطبيق وبصمة SHA-256 لشهادة التوقيع. وعند نجاح التحقق يفتح الرابط التطبيق مباشرةً. ومنذ Android 12 يُفتح رابط https غير المتحقَّق منه في المتصفح الافتراضي، إلا إذا وافق المستخدم على النطاق للتطبيق في إعداداته.
وتتبع الروابط العامة (Universal Links) في iOS الفكرة نفسها. يذكر التطبيق النطاق في صلاحية Associated Domains (applinks:shop.example.com)، وينشر الموقع ملف apple-app-site-association الذي يسمّي التطبيق والمسارات التي يعالجها. ومنذ iOS 14 تجلب الأجهزة هذا الملف عبر شبكة توصيل المحتوى (CDN) التابعة لـ Apple، وقد تحتاج هذه الشبكة إلى 24 ساعة لالتقاط أي تغيير، ويُنزّل الجهاز الملف عند تثبيت التطبيق ثم لا يبحث عن تحديث له إلا مرة في الأسبوع تقريباً؛ لذا بعد الإصلاح انتظر الشبكة ثم أعد تثبيت التطبيق على جهاز الاختبار. والرابط العام المكتوب في شريط عنوان Safari يفتح الموقع، وكذلك النقر على رابط من النطاق نفسه والمستخدم على ذلك الموقع في Safari: هذا سلوك المنصة وليس عيباً.
- يُقدَّم الملفان عبر HTTPS بشهادة صالحة ودون إعادة توجيه، ويُقدَّم
assetlinks.jsonبنوع المحتوىapplication/json. - كل نسخة Android موقّعة بمفتاح، والبصمة تعرّف شهادة ذلك المفتاح. ويجب أن تطابق البصمةُ المفتاحَ الذي وقّع النسخة المثبّتة: لنسخ التصحيح (Debug) ونسخ الإصدار والنسخ التي يعيد توقيعها Google Play App Signing بصمات مختلفة، ويمكن أن يسرد الملف أكثر من بصمة.
- ملف iOS بلا امتداد
.json، وتنطبق مساراته على كل نسخة مثبّتة من التطبيق، بما فيها النسخ القديمة. - الربط العميق المؤجَّل (Deferred deep linking)، أي الوصول إلى الهدف بعد تثبيت التطبيق من المتجر، ليس ميزة في المنصة. فإن طلبه المتطلب بناه فريقك أو حزمة SDK من طرف ثالث، ويحتاج إلى اختباراته الخاصة.
https://shop.example.com/.well-known/assetlinks.json (Android App Links)
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.shop",
"sha256_cert_fingerprints": ["14:6D:E9:...:2E:5F"]
}
}]
https://shop.example.com/.well-known/apple-app-site-association (iOS Universal Links)
{
"applinks": {
"details": [{
"appIDs": ["ABCDE12345.com.example.shop"],
"components": [{ "/": "/orders/*" }]
}]
}
}مثبّت أم لا، مسجّل الدخول أم لا
اختبر الرابط نفسه في عدة حالات، لأن كل حالة تسلك مساراً مختلفاً. غير مثبّت: يفشل رابط المخطط المخصص، بينما يفتح رابط https المتحقَّق منه الموقع على العنوان نفسه، فيجب أن يكون هذا العنوان صفحة حقيقية، ويُفضَّل أن تعرض طريقة للحصول على التطبيق، لا صفحة 404 أبداً. مثبّت لكن غير متحقَّق منه: يُفتح الموقع رغم وجود التطبيق، وهذه العلامة المعتادة لبصمة خاطئة أو ملف ربط معطّل. مثبّت بنسخة أقدم: يجب أن يعود التطبيق إلى شاشة معقولة عند مسار لا يعرفه، لا أن ينهار.
وطريقة تشغيل التطبيق مهمة أيضاً. فالرابط الذي يصل والتطبيق مغلق يشغّله من البداية، والذي يصل وهو في الخلفية أو يعرض شاشة أخرى يمرّ بشيفرة مختلفة. اختبر الحالتين، وتحقق إلى أين يقود زر الرجوع بعد ذلك.
ثم المستخدم. غير مسجّل الدخول: تظهر شاشة تسجيل الدخول، وبعد نجاحه تُفتح الشاشة المرتبطة لا الشاشة الرئيسية. مسجّل الدخول بصفته المالك: يُفتح السجل الصحيح. مسجّل الدخول بحساب مستخدم آخر: رسالة واضحة بأن المحتوى غير متاح ولا شيء من بيانات المالك، ويفرض الخادم ذلك ولا يكتفي التطبيق بالإخفاء. جلسة منتهية أو أول تشغيل بعد التثبيت: يجب ألا تبتلع إعادةُ المصادقة أو شاشاتُ التعريف بالتطبيق (Onboarding) أو طلبُ إذنٍ الرابطَ.
الإجابة: مخطط مخصص مثل
shop://سؤال 2: هل التطبيق مثبّت؟الإجابة: لا
النتيجة: لا يُفتح شيء، أو تظهر رسالة خطألا يوجد مسار بديل، فلا ترسل روابط المخطط إلى المستخدمينتنبيهالإجابة: نعم
النتيجة: يفتح تطبيقٌ الهدفدون تحقق: يستطيع تطبيق آخر تسجيل المخطط نفسه
الإجابة: رابط
https://من نوع App Link أو Universal Linkسؤال 3: هل التطبيق مثبّت؟الإجابة: لا
النتيجة: يُفتح الموقع على العنوان نفسهيجب أن تكون الصفحة موجودة وأن تعرض التطبيق؛ لا صفحة 404 أبداًالإجابة: نعم
سؤال 4: هل تحقق النظام من النطاق لهذه النسخة؟assetlinks.jsonفي Android وapple-app-site-associationفي iOSالإجابة: لا
النتيجة: يُفتح الموقع رغم أن التطبيق مثبّتعيب في الإعدادات: البصمة أو ملف مفقود أو إعادة توجيهفشلالإجابة: نعم
سؤال 5: هل المستخدم مسجّل الدخول؟الإجابة: لا
النتيجة: تسجيل الدخول ثم الشاشة المرتبطةلا الشاشة الرئيسيةالإجابة: نعم
سؤال 6: هل يحق لهذا المستخدم رؤية السجل؟الإجابة: لا
النتيجة: رسالة بأن المحتوى غير متاحلا شيء من بيانات السجل؛ والخادم يرفض الطلبالإجابة: نعم
النتيجة: الشاشة المرتبطة بالسجل الصحيحنجاح
يتبع فرع عدم التحقق سلوك Android 12 وما بعده؛ وفي إصدارات Android الأقدم قد يعرض رابط https غير متحقَّق منه نافذة اختيار بين التطبيق والمتصفح.
تشغيل الروابط والتحقق من الربط
انقر الروابط من القنوات التي يستخدمها المستخدمون فعلاً: تطبيق البريد، والرسائل النصية، وتطبيق دردشة، ورمز QR يُمسح بالكاميرا، وإشعار فوري. فبعض التطبيقات تفتح الروابط في متصفح داخلي خاص بها قد لا يسلّمها إلى تطبيقك، وتتبّع النقرات في البريد كثيراً ما يغلّف الرابط بإعادة توجيه عبر نطاق آخر غير مرتبط بتطبيقك. اختبر الرابط كما يُرسَل بالضبط، لا العنوان النظيف المكتوب في التذكرة.
وللفحوص السريعة القابلة للتكرار استخدم أدوات المنصة. في Android يطلق adb رابطاً ويترك للنظام اختيار التطبيق الذي يعالجه، ومنذ Android 12 يعرض حالة التحقق من النطاق ويعيد تشغيلها. وفي محاكي iOS يفتح simctl عنوان URL، أما على iPhone حقيقي فالصق الرابط في Notes أو Messages وانقر عليه.
# Android: fire the link and let the system choose the app (no package name)
adb shell am start -W -a android.intent.action.VIEW \
-c android.intent.category.BROWSABLE \
-d "https://shop.example.com/orders/A-1042"
# Android 12+: domain verification state, then a fresh verification
adb shell pm get-app-links com.example.shop
adb shell pm verify-app-links --re-verify com.example.shop
# iOS Simulator: open the link through the system
xcrun simctl openurl booted "https://shop.example.com/orders/A-1042"مثال تطبيقي: رابط الطلب في رسالة الشحن
المتطلب: «تتضمن رسالة الشحن رابطاً إلى الطلب. مع التطبيق يفتح الرابط تفاصيل الطلب، ومن دونه يفتح صفحة الطلب على الموقع». الرابط هو https://shop.example.com/orders/A-1042 والطلب يخص سارة. ابدأ بجدول قرار (Decision table) على أربعة شروط من نوع صحيح أو خطأ: مثبّت، ومتحقَّق منه، ومسجّل الدخول، والمالك. وكل عمود في الجدول توليفة واحدة من هذه الشروط الأربعة مع الشاشة التي يجب أن يفتحها الرابط، والأعمدة تعطي الحالات الأساسية؛ ويشرح درس اختبار جداول القرار كيف تبني الجدول وتدمج الأعمدة التي لا يغيّر فيها شرطٌ النتيجة. ثم أضف تنويعات حالة التشغيل والقناة إلى الأعمدة الأعلى مخاطرة بدلاً من مضاعفة كل شيء.
- مثبّت، وسارة مسجّلة الدخول، والتطبيق مغلق: يُفتح الطلب A-1042، ويقود زر الرجوع إلى قائمة الطلبات كما اتُّفق.
- الحالة نفسها والتطبيق في الخلفية على شاشة السلة: يُفتح الطلب A-1042 وتبقى السلة دون تغيير.
- مثبّت وغير مسجّل الدخول: تظهر شاشة تسجيل الدخول، وبعد دخول سارة يُفتح الطلب A-1042 لا الشاشة الرئيسية.
- مثبّت ومستخدم آخر مسجّل الدخول: رسالة بأن الطلب غير متاح، ولا بيانات من A-1042، وترفض الواجهة البرمجية (API) إرجاع بيانات الطلب A-1042.
- غير مثبّت: تُفتح صفحة الطلب على الموقع (بعد تسجيل الدخول على الويب) مع رابط إلى المتجر، ولا صفحة 404.
- نسخة موقّعة بمفتاح غير مذكور في
assetlinks.json: يُفتح الموقع ولا يُظهرpm get-app-linksالنطاق بالحالةverified. أبلغ عنه على الإعدادات لا على الشاشة. - معرّف مجهول أو فارغ أو طويل جداً (
/orders/A-9999و/orders/ومعرّف من 300 حرف): شاشة خطأ واضحة دون انهيار. - رسالة البريد الحقيقية، عبر إعادة توجيه تتبّع النقرات، في كل تطبيق بريد مدعوم: النتيجة نفسها كما مع الرابط النظيف.
أخطاء شائعة
- الاختبار والتطبيق مثبّت والمستخدم مسجّل الدخول فقط، وهي الحالة الوحيدة التي جرّبها المطورون أصلاً.
- إطلاق الروابط عبر
adbموجّهاً إلى حزمة التطبيق فقط، أو كتابتها في المتصفح، فيتجاوز ذلك معالجة المنصة للروابط أو يغيّرها؛ انقرها حيث ينقرها المستخدمون. - الإبلاغ عن «فُتح الموقع» كعيب في التطبيق دون التحقق من الربط أولاً؛ فالسبب عادةً ملف الربط أو البصمة أو إعادة توجيه.
- قبول الشاشة الرئيسية بعد تسجيل الدخول نتيجةً ناجحة: المستخدم أراد الطلب، فالوصول إلى أي مكان آخر فشل.
- نسيان نسخ التطبيق الأقدم، التي ما زالت تستقبل الروابط، وتستقبل في iOS ملف الربط الحالي أيضاً.
- الوثوق بمعاملات الرابط: يجب ألا يتجاوز الرابط تسجيل الدخول، أو يعرض بيانات مستخدم آخر، أو يدفع أو يحذف أو يرسل شيئاً دون تأكيد.
من منظور ضمان الجودة
اطلب مبكراً جرداً للروابط العميقة: كل رابط يرسله المنتج أو يطبعه، ونمط عنوانه، والشاشة الهدف، والمسار البديل (Fallback)، ومن يملك ملفات الربط. واتفق مع المطورين ومالك المنتج على وجهة زر الرجوع بعد الرابط، وما يظهر عند مسار مجهول، وهل الربط العميق المؤجَّل ضمن النطاق. وأضف فحص ملفي الربط إلى قائمة تحقق الإصدار، لأن نشر تحديث للموقع قد يعطّل الروابط دون أي تغيير في التطبيق.
وفي تقرير العيب سجّل الرابط كما أُرسل بالضبط، والقناة التي نُقر منها، والجهاز ونظام التشغيل ونسخة التطبيق ورقم البناء (Build)، وهل كان التطبيق مثبّتاً أو يعمل أو مغلقاً، والمستخدم المسجّل، وحالة التحقق. ولا تفحص السلوك الأمني، مثل رابط يتجاوز تسجيل الدخول، إلا في تطبيقات يملكها فريقك أو لديك إذن مكتوب باختبارها، وفق متطلبات التفاعل مع المنصة في MASVS التي يتبعها فريقك.
تمرين عملي
صمّم اختبارات الروابط العميقة لرسالة عرض ترويجي
يرسل متجر الرسالة النصية أدناه. العروض متاحة للجميع، لكن رمز الخصم لا يُطبَّق إلا على سلة مستخدم مسجّل الدخول، وقد ينتهي العرض قبل أن ينقر المستخدم الرابط.
صمّم حالات الاختبار: الحالات التي تجمع بينها، والنتيجة المتوقعة لكل منها، والفحوص التي تجريها قبل أن تنسب الفشل إلى التطبيق.
الرسالة النصية:
رمز خصم 10% بانتظارك: https://shop.example.com/offers/ramadan?code=SAVE10
القاعدة:
مع التطبيق: يفتح الرابط شاشة العرض ويطبّق الرمز على السلة.
بدون التطبيق: يفتح صفحة العرض على الموقع الإلكتروني.- اكتب الحالات: مثبّت أم لا، متحقَّق منه أم لا، التطبيق مغلق أم في الخلفية، المستخدم مسجّل الدخول أم لا.
- اكتب النتيجة المتوقعة لكل توليفة ذات معنى، بما في ذلك ما يحدث لرمز الخصم.
- أضف المدخلات غير الصالحة: رمز مفقود أو مجهول أو بأحرف صغيرة، وعرض انتهى.
- اكتب الفحوص التي تفصل خللاً في الإعدادات عن عيب في التطبيق.
إظهار الإجابة النموذجية
مثبّت ومسجّل الدخول، والتطبيق مغلق ثم في الخلفية: تُفتح شاشة العرض وتُظهر السلة الرمز SAVE10 مطبّقاً مرة واحدة؛ والنقر على الرابط مرة ثانية لا يطبّقه مرتين.
مثبّت وغير مسجّل الدخول: يُفتح العرض دون تسجيل دخول لأن العروض متاحة للجميع. وتطبيق الرمز يطلب تسجيل الدخول، وبعده يكون الرمز مطبّقاً ويعود المستخدم إلى العرض أو السلة، لا إلى الشاشة الرئيسية.
غير مثبّت: تُفتح صفحة العرض على الموقع مع الرمز، وطريقة للحصول على التطبيق، ولا صفحة 404. والاحتفاظ بالرمز عبر التثبيت من المتجر هو ربط عميق مؤجَّل يحتاج إلى متطلب خاص به.
المدخلات غير الصالحة: غياب المعامل code، ورمز مجهول، وsave10 بأحرف صغيرة (يُقبل أو يُرفض كما تنص القواعد)، وعرض منتهٍ، كلٌّ منها يعطي رسالة واضحة؛ والرمز الخاطئ لا يُفرغ السلة أبداً.
مثبّت لكن غير متحقَّق منه: تُفتح صفحة العرض على الموقع بدلاً من التطبيق، وهذا خلل في الإعدادات لا عيب في التطبيق. وقبل أن تنسب هذا الفشل أو غيره إلى التطبيق أجرِ الفحوص: انقر الرابط في تطبيق الرسائل لا في المتصفح؛ وشغّل pm get-app-links على Android؛ وتأكد أن ملفي الربط يُحمَّلان عبر HTTPS دون إعادة توجيه، وأن ملف iOS يغطي /offers/* وأن مرشّح النوايا في Android يعلن هذا المسار؛ وتتبّع أي أداة تقصير روابط بحثاً عن إعادة توجيه عبر نطاق آخر.
أسئلة شائعة
ما الفرق بين الرابط العميق وApp Link والرابط العام (Universal Link)؟
الرابط العميق أي رابط يفتح مكاناً محدداً في التطبيق. وAndroid App Links والروابط العامة في iOS روابط عميقة تستخدم عناوين https عادية على نطاق تحققت المنصة من ارتباطه بالتطبيق، فتفتح التطبيق إن كان مثبّتاً والموقع إن لم يكن. أما رابط المخطط المخصص مثل shop:// فرابط عميق بلا هذا التحقق.
لماذا يفتح الرابط الموقع والتطبيق مثبّت؟
غالباً لأن المنصة لم تتحقق من النطاق لهذه النسخة: البصمة في assetlinks.json لا تطابق مفتاح التوقيع، أو أحد الملفين مفقود أو غير صالح أو يُعاد توجيهه، أو النطاق غير مذكور في مرشّح النوايا أو في Associated Domains. وفي iOS هذا متوقع أيضاً إذا كُتب العنوان في Safari أو نُقر على صفحة من الموقع نفسه. تحقق من حالة الربط قبل الإبلاغ.
هل أستطيع اختبار الروابط العميقة على المحاكيات؟
كثير من الفحوص نعم: يعمل adb على محاكي Android ويعمل xcrun simctl openurl على محاكي iOS. لكن احتفظ بجولة واحدة على الأقل على أجهزة حقيقية مع القنوات الحقيقية، مثل تطبيقات البريد والمتصفحات الداخلية وماسح QR في الكاميرا والتثبيت من المتجر، لأنها تتصرف هناك بشكل مختلف أو لا توجد إلا هناك.
هل يجب أتمتة اختبارات الروابط العميقة؟
التوجيه نعم: يستطيع اختبار واجهة المستخدم فتح كل نمط رابط على محاكٍ والتحقق من الشاشة، فيكشف انحدار التوجيه في كل بناء، ويستطيع سكربت بسيط جلب ملفي الربط بعد كل نشر للموقع. أما سلوك القنوات ومسار عدم التثبيت فيبقيان يدويين عادةً ويُنفَّذان قبل الإصدار أو الحملة.
المراجع
- About App Linksروابط App Links في Android: روابط عميقة إلى موقعك يتحقق منها النظام عبر ملف Digital Asset Links (assetlinks.json)، فتفتح التطبيق مباشرةً.
- Allowing apps and websites to link to your contentالروابط العامة (Universal Links) على منصات Apple: روابط ويب عادية تفتح التطبيق إن كان مثبّتاً — بعد أن يتحقق النظام من ملف على موقعك — وتفتح الموقع إن لم يكن.
- OWASP Mobile Application Security Testing Guide (MASTG)دليل الاختبار المرافق لـ MASVS: تقنيات وحالات اختبار للتحقق من أمان تطبيقات الجوال.
- OWASP Mobile Application Security Verification Standard (MASVS)متطلبات OWASP الأمنية لتطبيقات الجوال، مجمّعة في مجالات ضبط مثل التخزين والتشفير والمصادقة والشبكة والتفاعل مع المنصة.
تدرّب بأدوات متقن
أنشئ جداول القرار من الشروط والإجراءات، وولّد جميع تركيبات القواعد، واختزل المستحيل منها.
تفكيك عنوان URL إلى البروتوكول والمضيف والمنفذ والمسار ومعاملات الاستعلام والجزء (fragment) وبيانات الاعتماد.
التحقق من صحة صياغة JSON مع تحديد مواقع الأخطاء بدقة واقتراحات مفيدة للإصلاح.
تتبّع سلاسل إعادة التوجيه (301/302/307/308) وعرض كل قفزة وحالتها وعنوان URL النهائي.
ولّد حالات اختبار منظّمة (المعرّف والعنوان والشروط المسبقة والخطوات والنتيجة المتوقّعة والأولوية) من وصف الميزة وقائمة الحقول.
ولّد أصغر مجموعة اختبارات زوجية (all-pairs) من المعاملات وقيمها.
للتعمّق أكثر
- سيناريوهات اختبار تطبيقات الجوالالإشعارات الفورية
- سيناريوهات اختبار تطبيقات الجوالالتثبيت والتحديث وإلغاء التثبيت وإعادة التثبيت
- اختبار تطبيقات الجوال: دليل عمليأمان الجوال: نظرة سريعة على MASVS وMASTG
- أتمتة اختبار تطبيقات الجوالكيف تعمل أتمتة اختبار الجوال: مقارنة Appium وEspresso وXCUITest، والمحددات بمعرّف إمكانية الوصول، ومصادر عدم الاستقرار، ومزارع الأجهزة وخطوط CI.
تقدّمك في هذا الموضوع
سجّل الدخول لتتابع تقدّمك
علّم الموضوعات مكتملة، وتابع من حيث توقفت، وافتح امتحانات التخصص.