اختبار تطبيقات Flutter

مقدمة إلى الاختبار في فلاتر

15 دقيقة الدرس 1 من 12

مقدمة إلى الاختبار في فلاتر

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

لماذا يهم الاختبار

كل تطبيق غير بسيط يتراكم فيه التعقيد بمرور الوقت. بدون اختبارات آلية، يستلزم التحقق من أن تغييراً في جزء من الكود لم يكسر جزءاً آخر استكشافاً يدوياً — وهو نهج لا يتوسع. توفر الاختبارات الآلية عدة فوائد ملموسة:

  • منع الانحدار: تعمل مجموعة الاختبارات في ثوانٍ وتكشف الأخطاء التي تسببها التغييرات الجديدة قبل وصولها إلى الإنتاج.
  • توثيق حي: الاختبارات ذات الأسماء الجيدة تصف السلوك المقصود من كودك، وتعمل كمواصفات قابلة للتنفيذ.
  • إعادة هيكلة أكثر أماناً: عندما تنجح الاختبارات بعد إعادة الهيكلة، يمكنك أن تكون واثقاً أن السلوك الملاحَظ لم يتغير.
  • تصحيح أسرع للأخطاء: اختبار الوحدة الفاشل يحدد الدالة المعطوبة فوراً، في حين أن تقرير خطأ الإنتاج يستلزم تحقيقاً مكثفاً.
  • ضغط التصميم: الكود الذي يصعب اختباره غالباً ما يكون ضعيف التصميم؛ كتابة الاختبارات مبكراً يشجع على الاقتران السهل ومبدأ المسؤولية الواحدة.
ملاحظة: يشحن فريق Flutter الإطار نفسه مع مجموعة اختبارات موسعة تضم أكثر من 100,000 اختبار. اعتماد نفس الانضباط في مشاريعك يعني أنك تستفيد من نفس الثقة التي يعتمدون عليها لشحن تحديثات الإطار.

هرم اختبار Flutter

يُنظّم Flutter الاختبارات في ثلاث طبقات، غالباً ما تُصوَّر كهرم. القاعدة واسعة (اختبارات كثيرة رخيصة) والقمة ضيقة (اختبارات قليلة مكلفة). لكل طبقة نطاق وسرعة وتكلفة مميزة:

  • اختبارات الوحدة (القاعدة): تختبر دالة أو طريقة أو فئة واحدة في عزل تام. تعمل على Dart VM دون أي عبء لإطار Flutter وتنتهي في ميلي ثوانٍ. استهدف المئات أو الآلاف من هذه الاختبارات.
  • اختبارات الودجت (الوسط): تختبر ودجتاً واحداً أو تركيبة صغيرة من الودجات. توفر Flutter بيئة اختبار خفيفة تحاكي دورة حياة الودجت دون الحاجة إلى جهاز حقيقي أو محاكٍ. إنها أبطأ من اختبارات الوحدة لكنها تعمل في ثوانٍ.
  • اختبارات التكامل (القمة): تختبر تدفقاً كاملاً عبر التطبيق الحقيقي على جهاز أو محاكٍ. تتحقق من أن جميع الطبقات — واجهة المستخدم ومنطق الأعمال والشبكة والتخزين — تعمل معاً. إنها الأكثر واقعية لكنها أيضاً الأبطأ والأكثر هشاشة.
نصيحة: تتبع مجموعة الاختبارات الصحية تقريباً نسبة 70/20/10: 70% اختبارات وحدة، 20% اختبارات ودجت، 10% اختبارات تكامل. عكس الهرم (اختبارات تكامل كثيرة واختبارات وحدة قليلة) يؤدي إلى خطوط CI بطيئة وهشة.

تهيئة حزمة الاختبار

مشاريع Flutter التي تُنشأ بـ flutter create تتضمن بالفعل حزمة flutter_test كـ dev_dependency في pubspec.yaml. لا تحتاج إلى تثبيت أي شيء إضافي لكتابة اختبارات الوحدة والودجت. القسم ذو الصلة في مشروع حديث الإنشاء يبدو كما يلي:

pubspec.yaml — قسم dev_dependencies

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^5.0.0

لاختبارات التكامل تحتاج أيضاً إلى حزمة integration_test، التي هي أيضاً جزء من Flutter SDK ولا تتطلب تنزيلاً منفصلاً:

إضافة integration_test إلى pubspec.yaml

dev_dependencies:
  flutter_test:
    sdk: flutter
  integration_test:
    sdk: flutter
  flutter_lints: ^5.0.0

بعد تعديل pubspec.yaml، شغّل flutter pub get لجلب رسم الاعتماديات المحدث.

هيكل مجلد الاختبار القياسي

بالاتفاقية، تعيش الاختبارات في مجلد test/ في جذر المشروع، ويعكس هيكل lib/. تعيش اختبارات التكامل في مجلد integration_test/ المجاور. التخطيط النموذجي يبدو كما يلي:

هيكل اختبار المشروع الموصى به

my_app/
├── lib/
│   ├── models/
│   │   └── cart.dart
│   ├── services/
│   │   └── cart_service.dart
│   └── widgets/
│       └── cart_badge.dart
├── test/
│   ├── models/
│   │   └── cart_test.dart
│   ├── services/
│   │   └── cart_service_test.dart
│   └── widgets/
│       └── cart_badge_test.dart
└── integration_test/
    └── cart_flow_test.dart

يكتشف مشغّل اختبارات Flutter تلقائياً أي ملف ينتهي اسمه بـ _test.dart داخل شجرة test/. يجعل عكس هيكل lib/ من السهل جداً إيجاد الاختبار لأي ملف مصدري معين.

تشغيل الاختبارات

يوفر Flutter CLI أوامر مباشرة لكل طبقة:

  • flutter test — يشغّل جميع اختبارات الوحدة والودجت في test/.
  • flutter test test/models/cart_test.dart — يشغّل ملف اختبار واحد.
  • flutter test --coverage — يشغّل الاختبارات ويولّد تقرير تغطية LCOV في coverage/lcov.info.
  • flutter test integration_test/cart_flow_test.dart — يشغّل اختبار تكامل على جهاز متصل أو محاكٍ.

اختبار وحدة بسيط — التحقق من دالة نقية

import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/models/cart.dart';

void main() {
  group('Cart', () {
    test('totalPrice returns sum of all item prices', () {
      final cart = Cart(items: [
        CartItem(name: 'Widget A', price: 9.99),
        CartItem(name: 'Widget B', price: 4.50),
      ]);

      expect(cart.totalPrice, closeTo(14.49, 0.001));
    });
  });
}
تحذير: لا تستورد أبداً dart:io أو تصل إلى نظام الملفات مباشرةً داخل اختبارات flutter_test — بيئة مضيف الاختبار لا تضمن مجلد عمل قابل للكتابة. استخدم بدائل وهمية في الذاكرة عوضاً عن ذلك.

ملخص

يمنحك هرم الاختبار الثلاثي في Flutter مفردات منظمة للتفكير في تغطية الاختبارات. اختبارات الوحدة توفر تغذية راجعة سريعة ومحددة على الدوال الفردية. اختبارات الودجت تتحقق من مكونات واجهة المستخدم دون جهاز حقيقي. اختبارات التكامل تتحقق من التدفقات الكاملة للمستخدم من البداية إلى النهاية. تأتي حزمتا flutter_test وintegration_test مع SDK، لذا التهيئة بسيطة: أضف الاعتماديات إلى pubspec.yaml، اعكس هيكل lib/ تحت test/، وشغّل flutter test للبدء. الاستثمار في الاختبارات من اليوم الأول لأي مشروع يحقق عوائد متضاعفة مع نمو قاعدة الكود.