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

اختبار التكامل باستخدام حزمة integration_test

16 دقيقة الدرس 10 من 12

اختبار التكامل باستخدام حزمة integration_test

تتحقق اختبارات التكامل من تطبيق Flutter بالكامل عن طريق تشغيله على جهاز حقيقي أو محاكٍ. على عكس اختبارات الوحدة (التي تختبر الدوال بشكل منعزل) أو اختبارات الودجات (التي تعمل في بيئة محاكاة)، تُمارس اختبارات التكامل المكدس الكامل للتطبيق — التنقل وإدارة الحالة والرسوم المتحركة وقنوات المنصة الحقيقية — مما يمنحك أعلى درجة من الثقة بأن تطبيقك يعمل بشكل صحيح لمستخدميك.

ملاحظة: حزمة integration_test هي حزمة الاختبار الرسمية من Flutter للاختبارات الشاملة من طرف إلى طرف. لقد حلت محل الحزمة القديمة flutter_driver وتتكامل مباشرةً مع واجهة برمجة التطبيقات flutter_test، لذا تعمل جميع المطابقات والباحثات التي تعرفها بدون تغيير.

إعداد حزمة integration_test

أضف الحزمة إلى ملف pubspec.yaml ضمن dev_dependencies:

dev_dependencies:
  flutter_test:
    sdk: flutter
  integration_test:
    sdk: flutter

تعيش ملفات اختبار التكامل في مجلد integration_test/ على المستوى الأعلى (ليس داخل test/). أنشئ ملف تشغيل مرافقاً في test_driver/integration_test.dart عندما تحتاج للتشغيل على Firebase Test Lab أو مع flutter drive:

// test_driver/integration_test.dart
import 'package:integration_test/integration_test_driver.dart';

Future<void> main() => integrationDriver();

IntegrationTestWidgetsFlutterBinding

يجب أن يستدعي كل ملف اختبار تكامل IntegrationTestWidgetsFlutterBinding.ensureInitialized() قبل أي مجموعة اختبار. يستبدل هذا رابط الاختبار الافتراضي بآخر يتواصل مع الجهاز المضيف (أو نظام CI) حتى يمكن الإبلاغ عن النتائج ولقطات الشاشة والجداول الزمنية للأداء خارجياً.

// integration_test/app_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart' as app;

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  group('اختبارات تكامل تطبيق العداد', () {
    testWidgets('يزيد العداد ويعرض القيمة المحدّثة',
        (WidgetTester tester) async {
      // تشغيل التطبيق الحقيقي
      app.main();
      await tester.pumpAndSettle();

      // التحقق من الحالة الأولية
      expect(find.text('0'), findsOneWidget);

      // تشغيل واجهة المستخدم: النقر على زر العائم
      await tester.tap(find.byTooltip('Increment'));
      await tester.pumpAndSettle();

      // التحقق من النتيجة
      expect(find.text('1'), findsOneWidget);
    });
  });
}
نصيحة: استدعِ app.main() (نقطة دخول main() الحقيقية) دائماً داخل جسم الاختبار، وليس على المستوى الأعلى. يضمن ذلك أن كل اختبار يبدأ من حالة تطبيق نظيفة عندما تحتوي مجموعة الاختبار على اختبارات متعددة.

تشغيل تدفقات متعددة الشاشات

القوة الحقيقية لاختبارات التكامل هي التحقق من التنقل وتدفق البيانات عبر الشاشات. استخدم tester.tap() وtester.enterText() وtester.pumpAndSettle() لمحاكاة رحلة مستخدم كاملة:

testWidgets('يمكن للمستخدم تسجيل الدخول ورؤية الشاشة الرئيسية',
    (WidgetTester tester) async {
  app.main();
  await tester.pumpAndSettle();

  // يجب أن نصل إلى شاشة تسجيل الدخول
  expect(find.text('Sign In'), findsOneWidget);

  // إدخال بيانات الاعتماد
  await tester.enterText(
    find.byKey(const Key('emailField')),
    'user@example.com',
  );
  await tester.enterText(
    find.byKey(const Key('passwordField')),
    'secret123',
  );

  // إرسال النموذج
  await tester.tap(find.byKey(const Key('signInButton')));
  await tester.pumpAndSettle();

  // التحقق من الانتقال إلى الشاشة الرئيسية
  expect(find.text('Welcome back!'), findsOneWidget);
  expect(find.byType(HomeScreen), findsOneWidget);
});

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

تُنفَّذ اختبارات التكامل باستخدام flutter test (مفضل) أو flutter drive:

  • على جهاز متصل أو محاكٍ: flutter test integration_test/app_test.dart
  • جميع اختبارات التكامل: flutter test integration_test/
  • مع flutter drive (لـ Firebase Test Lab): flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart
تحذير: تعمل اختبارات التكامل على جهاز حقيقي أو محاكٍ — وهي أبطأ بشكل كبير من اختبارات الوحدة أو الودجات (غالباً 30 إلى 120 ثانية لكل مجموعة). لا تستبدل اختبارات الوحدة والودجات باختبارات التكامل. استخدم اختبارات التكامل بحكمة للتدفقات الحرجة للمستخدمين: تسجيل الدخول والدفع والتهيئة الأولية وما إلى ذلك.

التقاط لقطات الشاشة أثناء الاختبارات

يمكن لحزمة integration_test التقاط لقطات الشاشة في أي نقطة خلال الاختبار. يُعدّ هذا ذا قيمة لا تُقدّر للكشف عن الانحدار البصري والإبلاغ في CI:

final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();

testWidgets('التقاط لقطة شاشة لشاشة تسجيل الدخول',
    (WidgetTester tester) async {
  app.main();
  await tester.pumpAndSettle();

  // التقاط لقطة شاشة قبل التفاعل
  await binding.takeScreenshot('login_screen');

  await tester.tap(find.byKey(const Key('signInButton')));
  await tester.pumpAndSettle();

  // التقاط لقطة شاشة بعد التنقل
  await binding.takeScreenshot('home_screen');
});

أفضل الممارسات

  • استخدم ValueKey أو Key على الودجات التفاعلية لجعل الباحثات متينة ولا تنكسر عند تغيير التسميات.
  • استخدم tester.pumpAndSettle() بدلاً من tester.pump() بعد الإجراءات التي تطلق رسوماً متحركة أو عمليات غير متزامنة؛ فهو ينتظر حتى تستقر جميع الإطارات.
  • بذر قاعدة البيانات أو محاكاة استدعاءات الشبكة على مستوى التطبيق (عبر حقن التبعيات أو علامات البيئة) لجعل اختبارات التكامل حتمية.
  • ابقِ كل اختبار تكامل مركزاً على قصة مستخدم واحدة — تدفق تسجيل دخول واحد، تدفق شراء واحد. تجنب الاختبارات الشاملة التي تتحقق من كل شيء.

الخلاصة

تتيح لك اختبارات التكامل مع حزمة integration_test تشغيل تطبيق Flutter حقيقي على جهاز، والتفاعل مع الودجات الفعلية عبر شاشات متعددة، والتأكد من السلوك من طرف إلى طرف. الخطوات الرئيسية هي: إضافة الحزمة كاعتمادية تطوير، استدعاء IntegrationTestWidgetsFlutterBinding.ensureInitialized()، تشغيل app.main() الحقيقي، استخدام باحثات ومطابقات flutter_test المألوفة، والتشغيل بـ flutter test integration_test/. احتفظ باختبارات التكامل لتدفقات المستخدمين الأكثر أهمية ودع اختبارات الوحدة والودجات تغطي الباقي.