Skip to main content

เนื้อหาภาษาไทย / Thai content

System guide Content reviewed

การ Testing แอป Flutter ด้วย Widget Tests และ Integration Tests

บทความนี้แนะนำการ Testing แอป Flutter ด้วย Widget Tests และ Integration Tests ซึ่งเป็นเครื่องมือสำคัญในการตรวจสอบความถูกต้องของโค้ด, ป้องกัน Regression, และเพิ่...

การ Testing แอป Flutter ด้วย Widget Tests และ Integration Tests

ในบทความก่อนหน้า เราได้เรียนรู้เกี่ยวกับการ Debugging แอป Flutter ด้วย Flutter DevTools ซึ่งเป็นเครื่องมือสำคัญในการค้นหาและแก้ไขข้อผิดพลาดในโค้ดของเรา ในบทความนี้ เราจะมาพูดถึงอีกหนึ่งเครื่องมือสำคัญที่จะช่วยให้แอปพลิเคชันของเรามีคุณภาพ นั่นคือ การ Testing แอป Flutter ด้วย Widget Tests และ Integration Tests

การ Testing เป็นกระบวนการตรวจสอบว่าโค้ดของเราทำงานได้อย่างถูกต้องตามที่คาดหวังไว้ การทำ Test ช่วยให้เรามั่นใจได้ว่าเมื่อเราทำการเปลี่ยนแปลงโค้ด จะไม่ส่งผลกระทบต่อส่วนอื่น ๆ ของแอปพลิเคชัน และยังช่วยลดโอกาสในการเกิดข้อผิดพลาดใน Production อีกด้วย

ทำไมต้อง Testing ใน Flutter?

  • ป้องกัน Regression: การ Testing ช่วยให้เราตรวจจับข้อผิดพลาดที่เกิดจากการเปลี่ยนแปลงโค้ดได้อย่างรวดเร็ว
  • ปรับปรุงคุณภาพโค้ด: การเขียน Test บังคับให้เราคิดถึงการออกแบบโค้ด ทำให้โค้ดของเรามีความชัดเจนและง่ายต่อการบำรุงรักษา
  • เพิ่มความมั่นใจ: การมี Test ที่ครอบคลุมช่วยให้เรามั่นใจในการ Deploy แอปพลิเคชัน

ประเภทของการ Testing ใน Flutter

ใน Flutter มีการ Testing หลายประเภท แต่ในบทความนี้เราจะเน้นที่ Widget Tests และ Integration Tests ซึ่งเป็นประเภทที่สำคัญและใช้กันบ่อย

  • Widget Tests: ทดสอบ Widget แต่ละตัวว่าทำงานถูกต้องหรือไม่ เช่น การแสดงผลข้อมูล การตอบสนองต่อการ Interact ของผู้ใช้
  • Integration Tests: ทดสอบการทำงานร่วมกันของ Widget หลายตัว หรือการทำงานร่วมกันของแอปพลิเคชันกับระบบภายนอก เช่น API หรือ Database

การเริ่มต้นใช้งาน Testing ใน Flutter

Flutter มี package ที่ชื่อว่า flutter_test ที่ช่วยให้เราเขียน Test ได้อย่างง่ายดาย package นี้ถูกติดตั้งมาพร้อมกับ Flutter SDK อยู่แล้ว ดังนั้นเราจึงสามารถเริ่มใช้งานได้ทันที

โครงสร้างไฟล์ Test

โดยทั่วไปแล้ว ไฟล์ Test จะถูกเก็บไว้ใน directory test โดยมีโครงสร้างไฟล์ดังนี้:

my_app/
  lib/
    main.dart
  test/
    widget_test.dart

ไฟล์ widget_test.dart จะเป็นไฟล์ที่เราเขียน Widget Tests

Widget Tests

Widget Tests ใช้สำหรับทดสอบ Widget แต่ละตัวว่าทำงานถูกต้องหรือไม่ โดยเราจะใช้ class WidgetTester ในการ Interact กับ Widget และตรวจสอบผลลัพธ์

ตัวอย่าง Widget Test

สมมติว่าเรามี Widget ที่แสดงข้อความ "Hello, World!" เราสามารถเขียน Widget Test เพื่อตรวจสอบว่า Widget นั้นแสดงข้อความถูกต้องหรือไม่ ดังนี้:

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/main.dart'; // สมมติว่า main.dart คือไฟล์ที่ Widget ของเราอยู่

void main() {
  testWidgets('MyWidget displays "Hello, World!"', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(MyApp()); // MyApp() คือ Widget ที่เราต้องการทดสอบ

    // Verify that our counter starts at 0.
    expect(find.text('Hello, World!'), findsOneWidget);
  });
}

คำอธิบายโค้ด:

  • testWidgets คือ function ที่ใช้สำหรับกำหนด Widget Test
  • WidgetTester คือ class ที่ใช้สำหรับ Interact กับ Widget
  • tester.pumpWidget ใช้สำหรับสร้าง Widget ที่เราต้องการทดสอบ
  • find.text('Hello, World!') ใช้สำหรับค้นหา Widget ที่มีข้อความ "Hello, World!"
  • findsOneWidget ใช้สำหรับตรวจสอบว่ามี Widget ที่ตรงกับเงื่อนไขการค้นหาเพียง 1 ตัว
  • expect ใช้สำหรับตรวจสอบว่าผลลัพธ์เป็นไปตามที่คาดหวัง

กรณีใช้งานจริงของ Widget Tests

1. ทดสอบการแสดงผลข้อมูล: ตรวจสอบว่า Widget แสดงข้อมูลถูกต้องหรือไม่ เช่น การแสดงชื่อผู้ใช้ รูปภาพ หรือรายละเอียดสินค้า

testWidgets('User name displays correctly', (WidgetTester tester) async {
  await tester.pumpWidget(
    MaterialApp(
      home: UserProfile(name: 'John Doe'), // UserProfile คือ Widget ที่แสดงข้อมูลผู้ใช้
    ),
  );

  expect(find.text('John Doe'), findsOneWidget);
});

2. ทดสอบการ Interact ของผู้ใช้: ตรวจสอบว่า Widget ตอบสนองต่อการ Interact ของผู้ใช้ถูกต้องหรือไม่ เช่น การกดปุ่ม การป้อนข้อความ หรือการเลือกรายการ

testWidgets('Button tap increments counter', (WidgetTester tester) async {
  await tester.pumpWidget(MaterialApp(home: CounterApp())); // CounterApp คือ Widget ที่มีปุ่มและตัวนับ

  // 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('1'), findsOneWidget);
});

ข้อผิดพลาดที่พบบ่อยในการเขียน Widget Tests

1. ลืมเรียก tester.pumpWidget: หากเราลืมเรียก tester.pumpWidget Widget ของเราจะไม่ถูกสร้างขึ้น ทำให้ Test ล้มเหลว

2. ใช้ find ไม่ถูกต้อง: หากเราใช้ find ไม่ถูกต้อง เช่น ค้นหา Widget ที่ไม่มีอยู่จริง Test ของเราก็จะล้มเหลว

3. ไม่รอ Animation เสร็จสิ้น: หาก Widget ของเรามีการ Animation เราจะต้องรอให้ Animation เสร็จสิ้นก่อนที่จะตรวจสอบผลลัพธ์

Integration Tests

Integration Tests ใช้สำหรับทดสอบการทำงานร่วมกันของ Widget หลายตัว หรือการทำงานร่วมกันของแอปพลิเคชันกับระบบภายนอก เช่น API หรือ Database

ในการเขียน Integration Tests เราจะต้องใช้ package integration_test ซึ่งเป็น package ที่แยกออกมาจาก flutter_test

การติดตั้ง integration_test

เพิ่ม dependency integration_test ลงในไฟล์ pubspec.yaml:

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

จากนั้นทำการ flutter pub get เพื่อติดตั้ง package

การตั้งค่า Integration Tests

สร้าง directory integration_test ใน root directory ของโปรเจกต์

my_app/
  lib/
    main.dart
  test/
    widget_test.dart
  integration_test/
    app_test.dart

ในไฟล์ integration_test/app_test.dart เราจะเขียน Integration Tests ของเรา

ตัวอย่าง Integration Test

สมมติว่าเรามีแอปพลิเคชันที่ดึงข้อมูลจาก API และแสดงผลบนหน้าจอ เราสามารถเขียน Integration Test เพื่อตรวจสอบว่าแอปพลิเคชันดึงข้อมูลและแสดงผลได้อย่างถูกต้องหรือไม่ ดังนี้:

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart'; // สมมติว่า main.dart คือไฟล์ที่แอปพลิเคชันของเราอยู่
import 'package:http/http.dart' as http;
import 'dart:convert';

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  testWidgets('App fetches data from API and displays it', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(MyApp());

    // Wait for the data to load.
    await tester.pumpAndSettle();

    // Verify that the data is displayed.
    expect(find.text('Data from API'), findsOneWidget); // สมมติว่า API return ข้อความนี้
  });
}

คำอธิบายโค้ด:

  • IntegrationTestWidgetsFlutterBinding.ensureInitialized() จำเป็นต้องเรียกก่อนที่จะเริ่ม Integration Tests
  • tester.pumpAndSettle() ใช้สำหรับรอให้แอปพลิเคชันโหลดข้อมูลและแสดงผล

กรณีใช้งานจริงของ Integration Tests

1. ทดสอบการทำงานกับ API: ตรวจสอบว่าแอปพลิเคชันสามารถดึงข้อมูลจาก API ได้อย่างถูกต้อง และแสดงผลข้อมูลได้อย่างถูกต้อง

2. ทดสอบการทำงานกับ Database: ตรวจสอบว่าแอปพลิเคชันสามารถอ่าน เขียน และอัปเดตข้อมูลใน Database ได้อย่างถูกต้อง

3. ทดสอบการ Navigation ระหว่างหน้าจอ: ตรวจสอบว่าแอปพลิเคชันสามารถ Navigate ระหว่างหน้าจอได้อย่างถูกต้อง

ข้อผิดพลาดที่พบบ่อยในการเขียน Integration Tests

1. ลืมเรียก IntegrationTestWidgetsFlutterBinding.ensureInitialized(): หากเราลืมเรียก IntegrationTestWidgetsFlutterBinding.ensureInitialized() Test ของเราจะไม่ทำงาน

2. ไม่รอให้แอปพลิเคชันโหลดข้อมูลเสร็จ: หากเราไม่รอให้แอปพลิเคชันโหลดข้อมูลเสร็จ Test ของเราอาจจะล้มเหลว

สรุป

การ Testing เป็นส่วนสำคัญในการพัฒนาแอปพลิเคชัน Flutter ที่มีคุณภาพ การเขียน Widget Tests และ Integration Tests ช่วยให้เรามั่นใจได้ว่าแอปพลิเคชันของเราทำงานได้อย่างถูกต้อง และช่วยลดโอกาสในการเกิดข้อผิดพลาดใน Production

ในบทความถัดไป เราจะมาเรียนรู้เกี่ยวกับการ Deploy แอป Flutter ไปยัง Android และ iOS

Office.in.th Custom Service

Want to build or adapt a system from this structure?

Office.in.th designs and develops enterprise web applications, API integrations, and public- and private-sector management systems, with long-term support.

See our software development service