Flutter : การทำ Testing ใน Flutter ตอนที่ 1
สวัสดีผู้อ่านครับ บล็อกนี้จะพามาลองเล่นเกี่ยวกับการเขียน test ใน Flutter ครับ ซึ่งตัวผมเองก็เพิ่งเริ่มศึกษา testing ใน Flutter ซึ่งในบล็อกนี้ผมจะใช้ Android Studio ในการรัน test และแสดงผลมาให้แล้ว ดังนั้นถ้าใครเขียนใน VS Code อาจจะต้องรันด้วย command แทน หน้าตาเลยอาจจะไม่เหมือนกัน เอาที่เราถนัดนะครับ
เริ่มต้น
เริ่มต้น pubspec.yaml ให้เพิ่ม flutter_test ใน dev_dependencies
dev_dependencies: flutter_test: sdk: flutter
Unit Test
unit test คือการทดสอบหน่วยย่อยที่สุด ก็คือ ทดสอบ method ทดสอบ logic การทำงาน
มาลองทำ unit test ใน dart กัน ก่อนอื่นสร้างคลาส model ขึ้นมา เช่น User
ภายในกมีข้อมูล เช่น ชื่อ อายุ
class User { String name; int age; User({this.name = "", this.age = 0}); }
ต่อมาสร้างไฟล์เพิ่ม เป็นไฟล์สำหรับเขียน test
การเขียน unit test ใน dart คล้ายกับใน java
แค่บอกว่า ต้องการผลลัพธ์อะไร คือคำสั่ง expect(… , …);
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_testing_app/user.dart'; void main() { test('User should update', () { final user = User(); user.name ="Benznest"; expect(user.name, "Benznest"); }); }
ซึ่งใน test เราสามารถเลือก scope test ได้ เช่น ถ้าคลิกขวาที่ กรอบนอกสุดก็จะ test ทั้งหมด แต่ถ้าคลิกข้างนอก ก็จะ test เฉพาะเคสนั้น
จากนั้นก็ลองรัน test
ขึ้นแบบนี้คือ ผ่าน
ลองกรณีที่ไม่ผ่านบ้าง เช่นผมกำหนดชื่อ ไม่ตรงกับ expect
void main() { test('Test User Name', () { final user = User(); user.name = "Benznest"; expect(user.name, "Nicolas"); }); }
ก็จะขึ้นว่า ผลลัพธ์ไม่ตรงนะ
Group test
ต่อมา เมื่อมีเคสมากขึ้น ก็จับรวมเป็นกลุ่ม หรือเป็นเรื่องๆไป โดยใช้ method group()
เช่น ผมมี 2 เคส แล้วเอามาไว้ใน group เดียวกัน
void main() { group('User', () { test('Test User Name', () { final user = User(); user.name = "Benznest"; expect(user.name, "Benznest"); }); test('Test User Age', () { final user = User(); user.age = 25; expect(user.age, 25); }); }); }
จากนั้นกด run test ที่ group
มันก็จะ test แต่ละเคสใน group ให้
ถ้ามีบางเคสใน group ไม่ผ่านก็จะขึ้นเตือน
Test Command
นอกจากการรัน test บน Android Studio แล้ว เราสามารถใช้คำสั่ง command รัน test ได้เช่นกัน
$ flutter test test/user_test.dart
Test Widget
เราสามารถเขียน Test ให้ Widget ได้เช่นกัน โดยยใช้ method testWidget()
ซึ่งจะมีคำสั่งที่สำคัญคือ .pumpWidget(…) มันคือการใส่ Widget เข้าไปใน tester
ค้นหา Widget ด้วย text
ตัวอย่างนี้ผมใส่ Widget ลงไป และมี Text ตัวนึงกำหนดค่าว่า ‘H’ (จะกำหนดอะไรก็ได้)
ซึ่งเราสามารถใช้คำสั่ง find.text(‘H’) นี้ค้นหา widget ได้ โดยมันจะวิ่งหา text ที่มีค่าตรงกันมาให้
ซึ่งพอเอามาใช้กับ expect โดย parameter ที่สองคือ findsOneWidget ก็คือเจอ 1 อัน
void main() { group('User', () { ... testWidgets('finds a Text Widget', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( home: Scaffold(body: Container(child: Center(child: Text('H')))), ), ); expect(find.text('H'), findsOneWidget); }); }); }
ถ้ามันเจอ Text ที่มีค่าตรงกัน 1 อันก็จะผ่าน
ลองกำหนดแบบไม่ตรงกัน
void main() { group('User', () { ... testWidgets('finds a Text Widget', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( home: Text('H'), ), ); expect(find.text('X'), findsOneWidget); }); }); }
ก็จะไม่ผ่าน พร้อมบอกว่า หา Widget ไม่เจอ แต่คาดหวังว่าจะมี 1 อัน ก็เลยไม่ผ่าน
ดังนั้น กรณีนี้ถ้าเราใช้ findsNothing ก็จะผ่านนั่นเอง
findsNothing ก็คือ ไม่เจออะไรเลย
void main() { group('User', () { ... testWidgets('finds a Text Widget', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( home: Text('H'), ), ); expect(find.text('X'), findsNothing); }); });
ค้นหา Widget ด้วย Key
ใน Flutter มี Key ที่สามารถระบุตัวตนของ Widget ได้ ดังนั้นถ้าเราระบุ key ให้ Widget
เราก็จะสามารถใช้ find.byKey() ได้
testWidgets('finds a Text Widget by Key', (WidgetTester tester) async { Key myKey = Key("K"); await tester.pumpWidget( MaterialApp( home: Text('H', key: myKey), ), ); expect(find.byKey(myKey), findsOneWidget); }); });
ค้นหา Widget ด้วย Widget
มีคำสั่งค้นหา Widget ด้วยตัวของ widget เอง แต่ต้องเป็น widget เดียวกันจริงๆ
testWidgets('finds a Text Widget by Widget', (WidgetTester tester) async { final Widget widget = Text(""); await tester.pumpWidget( MaterialApp( home: Container(child: Center(child: widget)), ), ); expect(find.byWidget(widget), findsOneWidget); });
ถ้าเป็นคนละตัวกัน แต่มีค่าเหมือนกัน ก็จะไม่ได้
Test action
ลอง test กดปุ่มหรือ กดบนหน้าจอบ้าง
ซึ่งจากตัวอย่างแอป counter ของ Flutter จะมี test มาให้ดังนี้ ก็คือ ก่อนกดปุ่มจะต้อง มีเลข 0 ไม่มี เลข 1
หลังกดปุ่มแล้วจะต้องมีเลข 1 ไม่มี 0 ถ้าตรงเคสก็ผ่าน
void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. await tester.pumpWidget(MyApp()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget); expect(find.text('1'), findsNothing); // Tap the '+' icon and trigger a frame. await tester.tap(find.byIcon(Icons.add)); await tester.pump(); // Verify that our counter has incremented. expect(find.text('0'), findsNothing); expect(find.text('1'), findsOneWidget); }); }
ดังนั้น สามารถเขียนแบบให้ลองกดปุ่ม 1,000 ครั้ง
แล้วเช็คเงื่อนไขแบบเมื่อกี้ ก็จะได้แบบนี้ ถ้าผ่านหมดก็ คือถูกต้องนั่นเอง
testWidgets('Counter increments 1000 times', (WidgetTester tester) async { await tester.pumpWidget(MyApp()); for (int i = 0; i < 1000; i++) { expect(find.text('$i'), findsOneWidget); // Tap the '+' icon and trigger a frame. await tester.tap(find.byIcon(Icons.add)); await tester.pump(); expect(find.text((i + 1).toString()), findsOneWidget); } });
สรุป
ในบล็อกนี้เป็นพื้นฐานการเขียน testing ใน Flutter ประกอบด้วย Unit test และ Widget test การรันก็ง่ายมากแค่กด run ใน test ที่ต้องการ หรือพิมพ์ command เอาก็ได้เช่นกัน
Credit
https://flutter.dev/docs/cookbook/testing/