دليلك إلى Widget Testing خطوة بخطوة
تخيل أن تطبيقك أصبح جاهزًا للعرض، الواجهة أنيقة، الأزرار تعمل، والنصوص تظهر كما أردت تمامًا.
لكن بعد تحديث بسيط، يتغير شيء صغير: زر لم يعد يعمل، أو نص اختفى، أو تفاعل لم يحدث كما كان.
النتيجة؟ تجربة المستخدم تتأثر، وقد لا تلاحظ ذلك إلا بعد فوات الأوان.
وهنا يظهر البطل المنقذ: اختبار الواجهات (Widget Testing).
إنه اختبار بصري وسلوكي يضمن أن واجهات تطبيقك تعمل كما صممتها تمامًا، حتى بعد مئات التعديلات.
ما هو اختبار الـ Widget في Flutter؟
اختبار الـ Widget هو نوع من الاختبارات يركّز على واجهة المستخدم، أي المكونات التي يراها ويتفاعل معها المستخدم:
الأزرار، النصوص، الصور، حقول الإدخال، القوائم، إلخ.
بعبارة بسيطة:
هدف اختبار الواجهة هو التأكد من أن الـ UI يبدو ويتصرف كما تتوقع.
يُطلق عليه في بعض الأطر الأخرى اسم اختبار المكونات (Component Test)، لأنه يختبر مكوّنات واجهة معينة داخل بيئة اختبارية مصغّرة تشبه التطبيق الحقيقي.
الفرق بين اختبار الوحدة واختبار الواجهة
| المقارنة | Unit Test | Widget Test |
|---|---|---|
| ما يختبره | دالة أو كود منطقي فقط | واجهة المستخدم وسلوكها |
| السرعة | أسرع جدًا ⚡ | أبطأ نسبيًا |
| البيئة | معزولة تمامًا | بيئة تحاكي واجهة Flutter |
| الهدف | التحقق من منطق الكود | التحقق من مظهر وتفاعل الواجهة |
الاختبار الواجهي لا يهتم فقط بالمنطق، بل بالتفاعل المرئي: هل الأزرار تظهر؟ هل تتجاوب مع اللمس؟ هل تنتقل الشاشة عند الضغط؟
إعداد أول اختبار Widget في Flutter
لنبدأ بمثال عملي بسيط:
افترض أن لدينا هذا الكود:
import 'package:flutter/material.dart';
class CounterWidget extends StatefulWidget {
@override
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int counter = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Count: $counter'),
ElevatedButton(
onPressed: () => setState(() => counter++),
child: const Text('Increment'),
),
],
);
}
}
الآن سنختبر سلوك هذا الـWidget:
هل يزيد العداد فعلاً عند الضغط على الزر؟
كتابة الاختبار:
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
import 'counter_widget.dart';
void main() {
testWidgets('يجب زيادة العداد عند الضغط على الزر', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(home: CounterWidget()));
// تحقق من القيمة المبدئية
expect(find.text('Count: 0'), findsOneWidget);
// اضغط على الزر
await tester.tap(find.byType(ElevatedButton));
await tester.pump();
// تحقق من النتيجة بعد الضغط
expect(find.text('Count: 1'), findsOneWidget);
});
}
في هذا الاختبار:
tester.pumpWidget()يقوم بتشغيل واجهتك داخل بيئة اختبارية مؤقتة.find.text()يبحث عن النصوص داخل الواجهة.tester.tap()يحاكي الضغط على الزر.tester.pump()يعيد رسم الواجهة بعد التحديث.
وبذلك، أنت تختبر المنظر والسلوك في آن واحد!
أدوات البحث داخل واجهات Flutter
Flutter توفر أدوات قوية للعثور على المكونات أثناء الاختبار:
| الأداة | الاستخدام |
|---|---|
find.text('Hello') | للعثور على Widget يحتوي على نص محدد |
find.byType(ElevatedButton) | للعثور على Widgets من نوع معين |
find.byKey(ValueKey('login_button')) | للعثور على عناصر محددة بمفتاح معين |
find.byIcon(Icons.add) | للعثور على عناصر تحتوي على أيقونة معينة |
استخدام هذه الأدوات يجعل اختباراتك أكثر دقة وسهولة في الصيانة.
اختبار التمرير والتفاعل والاتجاه
يمكنك أيضًا محاكاة سلوك المستخدم بالكامل داخل الاختبار، مثل:
await tester.drag(find.byType(ListView), const Offset(0, -300));
await tester.pumpAndSettle();
await tester.enterText(find.byType(TextField), 'Ahmed');
await tester.tap(find.text('Submit'));
بل ويمكنك اختبار اتجاه الشاشة (Orientation) أو تحولات الحالة (State) للتأكد من أن كل شيء يتفاعل كما هو متوقع.
أفضل الممارسات لكتابة اختبارات Widget فعالة
- استخدم أسماء وصفية توضح الهدف من الاختبار.
- اجعل كل اختبار يركز على سلوك واحد فقط.
- استخدم pumpAndSettle() عند وجود حركات أو انتقالات.
- لا تختبر مكونات Flutter الافتراضية، بل منطق تطبيقك أنت.
- استخدم Golden Tests للتحقق من المظهر البصري (لقطات الشاشة).
اختبارات Golden: اختبار الشكل بالصور
أحيانًا تريد التأكد أن واجهتك لم تتغير بصريًا بعد تعديل الكود.
يمكنك استخدام اختبارات Golden لمقارنة لقطة من الشاشة قبل وبعد التحديث.
مثلاً:
await expectLater(
find.byType(CounterWidget),
matchesGoldenFile('counter_golden.png'),
);
إذا تغيّر الشكل، سيفشل الاختبار — مما يساعدك على اكتشاف التغييرات غير المقصودة في التصميم.
الخلاصة
اختبار الـWidget هو أحد أقوى أسلحة مطوري Flutter للحفاظ على تجربة المستخدم.
فهو لا يتحقق فقط من المنطق، بل من الواجهة والتفاعل والمظهر النهائي.
ابدأ باختبارات بسيطة على الشاشات المهمة، وستندهش من مدى الثقة التي تضيفها إلى مشروعك.
✨ تذكّر: الكود الجيد يمكن أن يعمل… لكن الكود المختبَر هو الذي يصمد.

