ข้ามไปยังเนื้อหา

Testing

Flutter ให้เครื่องมือ test สี่แบบ ซึ่งแลกความเร็วกับความสมจริง ใช้แบบเร็วเยอะ ๆ และแบบช้าน้อย ๆ

flowchart TB
  integ["Integration tests
(whole app, real device) — few, slow"] --> widget["Widget tests
(one widget tree, no device) — many, fast"]
  widget --> unit["Unit tests
(pure Dart logic) — most, fastest"]
Flutter test pyramid
  • Unit test ตรวจ Dart ล้วน ๆ — function, class, logic ของ Cubit ไม่มี UI ไม่มี device เร็วระดับ millisecond
  • Widget test pump widget เข้า test harness แล้วโต้ตอบกับ widget นั้น (tap, พิมพ์, expect) — ไม่ต้องมี device จริง ยังเร็วอยู่ นี่คือจุด sweet spot เฉพาะของ Flutter
  • Integration test รันทั้งแอปบน device หรือ emulator จริง ขับแอปแบบ end to end — ช้า แต่สมจริงที่สุด
  • Golden test เก็บ pixel ที่ render ของ widget แล้วเทียบกับ reference image ที่ save ไว้ จับ visual regression
import 'package:test/test.dart';
test('cart total sums item prices', () {
final cart = Cart()..add(Item(price: 10))..add(Item(price: 5));
expect(cart.total, 15);
});

Dart ล้วน ๆ, expect ล้วน ๆ logic ส่วนใหญ่ของคุณ — pricing, validation, parsing, state transition — ควรเข้าถึงได้ด้วย test ที่ง่ายแบบนี้ นั่นคือเหตุผลที่ดีในการเก็บ logic ออกจาก widget

widget test ใช้ WidgetTester เพื่อ build widget, find element, act และ assert — ทั้งหมดอยู่ใน memory

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
testWidgets('tapping + increments the counter', (WidgetTester tester) async {
await tester.pumpWidget(const MyApp()); // build the widget tree
expect(find.text('0'), findsOneWidget); // initial state
await tester.tap(find.byIcon(Icons.add)); // interact
await tester.pump(); // rebuild after setState
expect(find.text('1'), findsOneWidget); // assert the new state
});

call สำคัญ: pumpWidget build tree, find หา widget, tap/enterText โต้ตอบ และ pump (หรือ pumpAndSettle สำหรับ animation) เดินหน้า frame เพื่อให้ rebuild เกิดขึ้น ไม่ต้องมี emulator — อันนี้รันในไม่กี่วินาที

testWidgets('button matches golden', (tester) async {
await tester.pumpWidget(const MyButton());
await expectLater(
find.byType(MyButton),
matchesGoldenFile('goldens/my_button.png'),
);
});

golden (snapshot) test ทรงพลังสำหรับ design system แต่เปราะข้าม platform และ font version — pin ให้ดีและ regenerate อย่างตั้งใจด้วย --update-goldens

widget test คืออะไร?
ใน widget test `pump` ทำอะไรหลังการโต้ตอบ?
ทำไมควรเก็บ business logic ออกจาก widget?
golden test จับอะไร?